src/app/services/sas-store.service.ts

Relationships

Used by

Depends on

No results matching.

Index

Properties
Methods

Constructor

constructor(sasService: SasService, helperService: HelperService, loggerService: LoggerService)
Parameters :
Name Type Optional
sasService SasService No
helperService HelperService No
loggerService LoggerService No

Methods

Public Async approveTable
approveTable(tableData: any, tableName: string, program: string)
Parameters :
Name Type Optional
tableData any No
tableName string No
program string No
Returns : unknown
Public Async callService
callService(tableData: Array, tableName: string, program: string, libds: string)

Wrapper for making request to service Should be removed, as it's redundant now TODO: Refactor to call editors/getdata directly

Parameters :
Name Type Optional
tableData Array<any> No
tableName string No
program string No
libds string No
Returns : unknown
Public checkOperator
checkOperator(operator: any, value: any, type: string)
Parameters :
Name Type Optional
operator any No
value any No
type string No
Returns : { value: any; operator: any; }
Public createFilterQueryTable
createFilterQueryTable(filterQuery: FilterQuery)

This function creates Filter Query Table

Parameters :
Name Type Optional Description
filterQuery FilterQuery No

an object of type FilterQuery

Returns : QueryClause[]

array of QueryClause

Public Async getApprovals
getApprovals(tableData: any, tableName: string, program: string)

Sending request to 'approvers/getapprovals' to fetch approvals list

Parameters :
Name Type Optional Description
tableData any No

sending to backend table data

tableName string No

sending to backend table name

program string No

service to run request on

Returns : unknown

HTTP Response

Public Async getChangeInfo
getChangeInfo(tableId: any)
Parameters :
Name Type Optional
tableId any No
Returns : unknown
Public Async getCols
getCols(libds: string)

Column metadata for the filter picker, read from the service.

The picker's variable list normally comes from the columns that arrived with the table (see setQueryVariablesFromTable). This is the fallback for when there is no table - public/getcols reads the metadata only.

public/getcols speaks the SAS vocabulary: TYPE is C/N and DDTYPE separates DATE, DATETIME and TIME. The picker keys its operator set (query.component), its variable highlight and its value quoting (sas-store) on the client vocabulary instead - TYPE is exactly 'num' or 'char' - which mergeColsRules derives from $dataFormats on the normal path. That pass never runs for this fallback, so map TYPE here, with the same rule: the date and time types are numeric.

Parameters :
Name Type Optional
libds string No
Returns : unknown
Public Async getHistory
getHistory(tableData: any, tableName: string, program: string)
Parameters :
Name Type Optional
tableData any No
tableName string No
program string No
Returns : unknown
Public Async getQueryValues
getQueryValues(variable: string, dataset: string, filterQuery: any[], STARTROW?: number, ROWS?: number)
Parameters :
Name Type Optional
variable string No
dataset string No
filterQuery any[] No
STARTROW number Yes
ROWS number Yes
Returns : unknown
Public Async getSubmitts
getSubmitts()
Returns : unknown

All submits

Public Async getXLMapRules
getXLMapRules(id: string)
Parameters :
Name Type Optional
id string No
Returns : unknown
Public handoffSubmits
handoffSubmits(fromsas: Array | undefined)

Carries the fetched submit queue over to the details URL navigation, so the re-created submit list component does not fetch it again.

Parameters :
Name Type Optional
fromsas Array<any> | undefined No
Returns : void
Public initializeGlobalFilterClause
initializeGlobalFilterClause(page: string, cols: Col[])

This helper function initializes global filter when user load the filtered url

Parameters :
Name Type Optional Description
page string No
cols Col[] No

array of columns metadata

Returns : void
Public Async openTable
openTable(tableId: string)
Parameters :
Name Type Optional
tableId string No
Returns : unknown
parseFormats
parseFormats($dataFormats: $DataFormats)
Parameters :
Name Type Optional
$dataFormats $DataFormats No
Returns : { [key: string]: any; }
Public Async refreshLibInfo
refreshLibInfo(libref: string)
Parameters :
Name Type Optional
libref string No
Returns : unknown
Public Async rejecting
rejecting(tableData: any, tableName: string, program: string)
Parameters :
Name Type Optional
tableData any No
tableName string No
program string No
Returns : unknown
Public Async removeClause
removeClause()
Returns : unknown
Public Async saveQuery
saveQuery(libds: string, filterQuery: QueryClause[])
Parameters :
Name Type Optional
libds string No
filterQuery QueryClause[] No
Returns : unknown
Public Async sendDetails
sendDetails(detail: any, index: any, data: any)

Interceptor for loading of the submitted details

Parameters :
Name Type Optional Description
detail any No

submitter

index any No

submitter index

data any No

submit data

Returns : any
setQueryVariables
setQueryVariables(dataset: string, cols: any)
Parameters :
Name Type Optional
dataset string No
cols any No
Returns : void
Public Async setQueryVariablesFromTable
setQueryVariablesFromTable(libds: string, cols: any[])

Feeds the filter picker its variable list.

cols is the column metadata that came back with the table, which costs nothing extra and is the normal path. When the table was not returned - the EDIT/VIEW row and cell limits abort the request before the data arrives - that list is empty, which is what makes "please filter and try again" unactionable: the picker has nothing to offer. Fall back to public/getcols, which reads the column metadata without the rows.

Parameters :
Name Type Optional
libds string No
cols any[] No
Returns : any
setSubmitReady
setSubmitReady()
Returns : void
Public Async showDiffs
showDiffs(tableData: any, tableName: string, program: string)
Parameters :
Name Type Optional
tableData any No
tableName string No
program string No
Returns : unknown
Public specConversion
specConversion(spec: any)
Parameters :
Name Type Optional
spec any No
Returns : SpecObj
Public takeSubmitsHandoff
takeSubmitsHandoff()

Returns the submit queue carried by the current navigation, if any, and clears it - it is only valid for the navigation that set it.

Returns : Array | null
Public Async updateTable
updateTable(tableParams: any, tableData: any, tableName: string, program: string, $dataFormats: $DataFormats | null, suppressErrorSuccessMessages?: boolean, adapterConfig?: any)

Calling editors/stagedata - saving table data, sending request to backend

Parameters :
Name Type Optional Description
tableParams any No

params to send to backend

tableData any No

data to be updated

tableName string No

name of the table to be updated

program string No

service against which we send request

$dataFormats $DataFormats | null No

column data formats recieved from backend, sending it back

suppressErrorSuccessMessages boolean Yes
adapterConfig any Yes
Returns : Promise<RequestWrapperResponse<EditorsStageDataSASResponse>>

adapter.request() response

Public Async viewData
viewData(libDataset: any, filter_pk: any)
Parameters :
Name Type Optional
libDataset any No
filter_pk any No
Returns : unknown
Public Async viewDataSearch
viewDataSearch(searchVal: string, numeric: boolean, libDataset: any, filter_pk: any)
Parameters :
Name Type Optional Default value
searchVal string No
numeric boolean No false
libDataset any No
filter_pk any No
Returns : unknown
Public viewLibs
viewLibs()
Returns : any

All libraries

Public Async viewTables
viewTables(lib: any)
Parameters :
Name Type Optional
lib any No
Returns : unknown
Public whereClauseCreator
whereClauseCreator(clauses: any, groupOperator: string, libds: string)
Parameters :
Name Type Optional
clauses any No
groupOperator string No
libds string No
Returns : { whereClause: string; string: string; }

Properties

Public changedTable
Type : Subject<any>
Default value : new Subject<any>()
Public columns
Type : Subject<any>
Default value : new Subject<any>()
Public details
Type : Subject<any>
Default value : new Subject<any>()
Public diffs
Type : Subject<any>
Default value : new Subject<any>()
Public filter
Type : Subject<any>
Default value : new Subject<any>()
Private libsPromise
Type : Promise<any> | null
Default value : null
Public query
Type : Subject<any>
Default value : new Subject<any>()
Public removeQuery
Type : Subject<any>
Default value : new Subject<any>()
Public response
Type : Subject<any>
Default value : new Subject<any>()
Public setSubmit
Type : Subject<any>
Default value : new Subject<any>()
Public setSubmitList
Type : Subject<any>
Default value : new Subject<any>()
Private submitsHandoff
Type : Array<any> | null
Default value : null

Submit queue payload carried across the navigation from the submit list to a details URL - the re-created list component renders it without fetching the queue again.

Public submittDetail
Type : Subject<any>
Default value : new Subject<any>()
Public values
Type : Subject<any>
Default value : new Subject<any>()
import { Injectable } from '@angular/core'
import { Subject } from 'rxjs'

import { SpecObj } from '../interfaces'
import { SasService } from './sas.service'
import { HelperService } from './helper.service'

import { QueryClause } from '../models/TableData'
import { globals } from '../_globals'
import { FilterClause, FilterGroup, FilterQuery } from '../models/FilterQuery'
import {
  $DataFormats,
  EditorsGetDataSASResponse,
  EditorsGetDataServiceResponse
} from '../models/sas/editors-getdata.model'
import { LoggerService } from './logger.service'
import { isSpecialMissing } from '@sasjs/utils/input/validators'
import { Col } from '../shared/dc-validator/models/col.model'
import { get } from 'lodash-es'
import { EditorsStageDataSASResponse } from '../models/sas/editors-stagedata.model'
import { RequestWrapperResponse } from '../models/request-wrapper/RequestWrapperResponse'

@Injectable()
export class SasStoreService {
  public response: Subject<any> = new Subject<any>()
  public changedTable: Subject<any> = new Subject<any>()
  public details: Subject<any> = new Subject<any>()
  public diffs: Subject<any> = new Subject<any>()
  public columns: Subject<any> = new Subject<any>()
  public values: Subject<any> = new Subject<any>()
  public filter: Subject<any> = new Subject<any>()
  public query: Subject<any> = new Subject<any>()
  public submittDetail: Subject<any> = new Subject<any>()
  public removeQuery: Subject<any> = new Subject<any>()
  public setSubmit: Subject<any> = new Subject<any>()
  public setSubmitList: Subject<any> = new Subject<any>()

  /**
   * Submit queue payload carried across the navigation from the submit
   * list to a details URL - the re-created list component renders it
   * without fetching the queue again.
   */
  private submitsHandoff: Array<any> | null = null

  constructor(
    private sasService: SasService,
    private helperService: HelperService,
    private loggerService: LoggerService
  ) {}

  /**
   * Wrapper for making request to service
   * Should be removed, as it's redundant now
   * TODO: Refactor to call editors/getdata directly
   * @param tableData
   * @param tableName
   * @param program
   * @param libds
   * @returns
   */
  public async callService(
    tableData: Array<any>,
    tableName: string,
    program: string,
    libds: string
  ) {
    const tables: any = {}
    tables[tableName] = [tableData]
    const res: RequestWrapperResponse<EditorsGetDataSASResponse> =
      await this.sasService.request(program, tables)
    const response: EditorsGetDataServiceResponse = {
      data: res.adapterResponse,
      libds: libds
    }
    return response
  }

  /**
   * Calling editors/stagedata - saving table data, sending request to backend
   * @param tableParams params to send to backend
   * @param tableData data to be updated
   * @param tableName name of the table to be updated
   * @param program service against which we send request
   * @param $dataFormats column data formats recieved from backend, sending it back
   * @returns adapter.request() response
   */
  public async updateTable(
    tableParams: any,
    tableData: any,
    tableName: string,
    program: string,
    $dataFormats: $DataFormats | null,
    suppressErrorSuccessMessages?: boolean,
    adapterConfig?: any
  ): Promise<RequestWrapperResponse<EditorsStageDataSASResponse>> {
    // add sp as third argument of createData call

    let tables: any = {
      jsdata: tableData
    }

    if ($dataFormats) {
      let formats = this.parseFormats($dataFormats)

      tables.$jsdata = { formats: formats }
    }

    tables[tableName] = [tableParams]

    let res = await this.sasService.request<EditorsStageDataSASResponse>(
      program,
      tables,
      adapterConfig,
      {
        suppressErrorAbortModal: suppressErrorSuccessMessages,
        suppressSuccessAbortModal: suppressErrorSuccessMessages
      }
    )

    return res
  }

  /**
   * Sending request to 'approvers/getapprovals' to fetch approvals list
   * @param tableData sending to backend table data
   * @param tableName sending to backend table name
   * @param program service to run request on
   * @returns HTTP Response
   */
  public async getApprovals(
    tableData: any,
    tableName: string,
    program: string
  ) {
    let tables: any = {}
    tables[tableName] = [tableData]
    return (await this.sasService.request(program, tables)).adapterResponse
  }

  /**
   * Interceptor for loading of the submitted details
   * @param detail submitter
   * @param index submitter index
   * @param data submit data
   */
  public async sendDetails(detail: any, index: any, data: any) {
    let details = Object.assign({ sub: true }, detail)
    let subData = data[index]
    let allData = {
      data: subData,
      viewData: details
    }
    this.submittDetail.next(allData)
  }

  /**
   *
   * @returns All submits
   */
  public async getSubmitts() {
    return (await this.sasService.request('editors/getsubmits', null))
      .adapterResponse
  }

  /**
   * Carries the fetched submit queue over to the details URL navigation,
   * so the re-created submit list component does not fetch it again.
   */
  public handoffSubmits(fromsas: Array<any> | undefined) {
    this.submitsHandoff = fromsas || null
  }

  /**
   * Returns the submit queue carried by the current navigation, if any,
   * and clears it - it is only valid for the navigation that set it.
   */
  public takeSubmitsHandoff(): Array<any> | null {
    const fromsas = this.submitsHandoff
    this.submitsHandoff = null
    return fromsas
  }

  private libsPromise: Promise<any> | null = null

  /**
   *
   * @returns All libraries
   */
  public viewLibs() {
    if (!this.libsPromise) {
      this.libsPromise = this.sasService
        .request('public/viewlibs', null)
        .then((res: any) => res.adapterResponse)
        .catch((err: any) => {
          this.libsPromise = null
          throw err
        })
    }
    return this.libsPromise
  }

  public async refreshLibInfo(libref: string) {
    const data = {
      lib2refresh: [{ libref }]
    }

    return (await this.sasService.request('public/refreshlibinfo', data))
      .adapterResponse
  }

  public async viewTables(lib: any) {
    let tables = { SASControlTable: [{ MPLIB: lib }] }
    return (await this.sasService.request('public/viewtables', tables))
      .adapterResponse
  }

  public async viewData(libDataset: any, filter_pk: any) {
    let tables = {
      SASControlTable: [{ LIBDS: libDataset, FILTER_RK: filter_pk }]
    }
    return (await this.sasService.request('public/viewdata', tables))
      .adapterResponse
  }

  public async viewDataSearch(
    searchVal: string,
    numeric: boolean = false,
    libDataset: any,
    filter_pk: any
  ) {
    let SEARCHTYPE = numeric ? 'NUM' : 'CHAR'

    let tables = {
      SASControlTable: [
        {
          SEARCHTYPE: searchVal.length > 0 ? SEARCHTYPE : 'NONE',
          SEARCHVAL: searchVal,
          LIBDS: libDataset,
          FILTER_RK: filter_pk
        }
      ]
    }

    return (await this.sasService.request('public/viewdata', tables))
      .adapterResponse
  }

  public async getXLMapRules(id: string) {
    const tables = {
      getxlmaps_in: [{ XLMAP_ID: id }]
    }
    return (await this.sasService.request('editors/getxlmaps', tables))
      .adapterResponse
  }

  public async showDiffs(tableData: any, tableName: string, program: string) {
    let tables: any = {}
    tables[tableName] = [tableData]
    return (
      await this.sasService.request(program, tables, {
        useComputeApi: null // Using WEB APPROACH as a temporary workaround until VIYA JES API is fixed. For other server types then VIYA this is not applicable
      })
    ).adapterResponse
  }
  public async rejecting(tableData: any, tableName: string, program: string) {
    let tables: any = {}
    tables[tableName] = [tableData]
    return (
      await this.sasService.request(program, tables, {
        useComputeApi: null // Using WEB APPROACH as a temporary workaround until VIYA JES API is fixed. For other server types then VIYA this is not applicable
      })
    ).adapterResponse
  }

  public async approveTable(
    tableData: any,
    tableName: string,
    program: string
  ) {
    let tables: any = {}
    tables[tableName] = [tableData]

    return (await this.sasService.request(program, tables)).adapterResponse
  }

  public async getHistory(tableData: any, tableName: string, program: string) {
    let tables: any = {}
    tables[tableName] = [tableData]
    return (await this.sasService.request(program, tables)).adapterResponse
  }

  setQueryVariables(dataset: string, cols: any) {
    let columnsData: any = { data: { cols: cols }, libds: dataset }

    this.columns.next(columnsData)
  }

  /**
   * Column metadata for the filter picker, read from the service.
   *
   * The picker's variable list normally comes from the columns that arrived
   * with the table (see setQueryVariablesFromTable). This is the fallback for
   * when there is no table - public/getcols reads the metadata only.
   *
   * public/getcols speaks the SAS vocabulary: TYPE is C/N and DDTYPE separates
   * DATE, DATETIME and TIME. The picker keys its operator set (query.component),
   * its variable highlight and its value quoting (sas-store) on the client
   * vocabulary instead - TYPE is exactly 'num' or 'char' - which
   * mergeColsRules derives from $dataFormats on the normal path. That pass
   * never runs for this fallback, so map TYPE here, with the same rule: the
   * date and time types are numeric.
   */
  public async getCols(libds: string) {
    const tables = { iwant: [{ libds: libds }] }

    const res = await this.sasService.request('public/getcols', tables)

    const cols = res.adapterResponse.cols || []

    return cols.map((col: any) => ({
      ...col,
      TYPE:
        ['DATE', 'DATETIME', 'TIME'].includes(col.DDTYPE) || col.TYPE === 'N'
          ? 'num'
          : 'char'
    }))
  }

  /**
   * Feeds the filter picker its variable list.
   *
   * `cols` is the column metadata that came back with the table, which costs
   * nothing extra and is the normal path. When the table was not returned - the
   * EDIT/VIEW row and cell limits abort the request before the data arrives -
   * that list is empty, which is what makes "please filter and try again"
   * unactionable: the picker has nothing to offer. Fall back to
   * public/getcols, which reads the column metadata without the rows.
   */
  public async setQueryVariablesFromTable(libds: string, cols: any[]) {
    if (cols && cols.length > 0) {
      this.setQueryVariables(libds, cols)
      return
    }

    const fallback = await this.getCols(libds)

    /*
      public/getcols emits an empty cols table when the dataset has no
      metadata.  Publishing that would push an empty array into the filter
      picker, whose subscriber reads cols[0].NAME - the throw is caught, but
      the picker is left half-updated (this.cols assigned, the filter.cols
      assignment below the throwing line never run).  There is nothing to
      offer, so leave the picker as it was.
    */
    if (!fallback || fallback.length === 0) return

    this.setQueryVariables(libds, fallback)
  }

  public async getChangeInfo(tableId: any) {
    let obj = { TABLE: tableId }
    let table = { SASControlTable: [obj] }
    return (await this.sasService.request('public/getchangeinfo', table))
      .adapterResponse
  }

  public async getQueryValues(
    variable: string,
    dataset: string,
    filterQuery: any[],
    STARTROW?: number,
    ROWS?: number
  ) {
    let tables: any = {
      iwant: [
        {
          libds: dataset,
          col: variable,
          ...(STARTROW && { STARTROW: STARTROW }), //add only if present
          ...(ROWS && { ROWS: ROWS }) //add only if present
        }
      ]
    }

    if (filterQuery.length > 0) {
      tables.FILTERQUERY = filterQuery
    }

    return (
      await this.sasService
        .request('public/getcolvals', tables)
        .catch((er: any) => {
          throw er
        })
    ).adapterResponse
  }

  public async saveQuery(libds: string, filterQuery: QueryClause[]) {
    let tables = {
      iwant: [{ filter_table: libds }],
      filterquery: filterQuery
    }

    const res = await this.sasService.request('public/validatefilter', tables)

    this.filter.next(res)

    return res.adapterResponse
  }

  public async openTable(tableId: string) {
    let tables = { iwant: [{ table_id: tableId }] }
    return (await this.sasService.request('auditors/getstagetable', tables))
      .adapterResponse
  }

  public checkOperator(operator: any, value: any, type: string) {
    let val
    let operators = ['=', '>', '<', '<=', '>=', 'ne']
    switch (operator) {
      case 'BETWEEN':
        if (value instanceof Array) {
          if (value[0] !== '' && value[1] !== '') {
            val = value[0] + ' AND ' + value[1]
          } else if (value[0] !== '' && value[1] === '') {
            value[1] = '.'
            val = value[0] + ' AND ' + value[1]
          } else if (value[0] === '' && value[1] !== '') {
            value[0] = '.'
            val = value[0] + ' AND ' + value[1]
          } else {
            value[0] = '.'
            value[1] = '.'
            val = value[0] + ' AND ' + value[1]
          }
        }
        break
      case 'IN':
      case 'NOT IN':
        let arr = []
        if (typeof value !== 'undefined') {
          for (let ind = 0; ind < value.length; ind++) {
            if (value[ind].checked === true) {
              if (type === 'char') {
                if (typeof value[ind].val === 'string') {
                  value[ind].val = value[ind].val.replace("'", "''")
                }

                arr.push("'" + value[ind].val + "'")
              } else {
                if (value[ind].val === null) {
                  value[ind].val = '.'
                }
                arr.push(value[ind].val)
              }
            }
          }
        }
        value = '(' + arr + ')'
        break
      case 'CONTAINS':
        operator = '?'
        break
      case 'NOT EQUAL':
        operator = 'ne'
        break
      default:
        break
    }

    if (type === 'num' && value === null) {
      value = '.'
    }

    if (value === 'Please select value') {
      value = ''
    }

    if (operators.indexOf(operator) !== -1 && type === 'num' && value === '') {
      value = '.'
    }

    if (operator === 'BETWEEN') {
      return { value: val, operator: operator }
    } else {
      return { value: value, operator: operator }
    }
  }

  public whereClauseCreator(
    clauses: any,
    groupOperator: string,
    libds: string
  ) {
    let whereString: string = ''
    let clausesArr = clauses.queryObj
    let clausesLogic = clauses.groupLogic
    let opr
    let where = ''
    let clauseArr = []

    for (let index = 0; index < clauses.queryObj.length; index++) {
      let string = ''
      let clause = clauses.queryObj[index]

      for (let ind = 0; ind < clause.elements.length; ind++) {
        let query = clause.elements[ind]

        if (ind < clause.elements.length - 1) {
          opr = clause.clauseLogic
        } else {
          opr = ''
        }

        let val: any

        for (let k = 0; k < query.values.length; k++) {
          if (
            typeof query.value === 'string' &&
            typeof query.values[k].formatted === 'number'
          ) {
            if (query.value === JSON.stringify(query.values[k].formatted)) {
              val = query.values[k].unformatted
            }
          } else {
            let dropValue =
              typeof query.values[k].formatted !== 'number' &&
              query.values[k].formatted !== null
                ? query.values[k].formatted.trim()
                : query.values[k].formatted

            if (query.value === dropValue) {
              val = query.values[k].unformatted
            }
          }

          let operators = ['=', '>', '<', '<=', '>=', 'ne']

          if (
            query.value === 'Please select value' &&
            operators.indexOf(query.operator) !== -1
          ) {
            val = ''
          }

          if (
            query.operator === 'CONTAINS' ||
            query.operator === 'LIKE' ||
            query.operator === 'BEGINS_WITH'
          ) {
            val = query.value
          }
        }

        if (!val) {
          val = query.value
        }

        if (query.value instanceof Array && query.operator === 'BETWEEN') {
          val = []
          val.push(query.value[0])
          val.push(query.value[1])
          for (let i = 0; i < query.values.length; i++) {
            if (val[0] === query.values[i].formatted) {
              val[0] = query.values[i].unformatted
            }
            if (val[1] === query.values[i].formatted) {
              val[1] = query.values[i].unformatted
            }
          }
        }

        if (
          query.value instanceof Array &&
          (query.operator === 'IN' || query.operator === 'NOT IN')
        ) {
          val = []
          val = query.value
          for (let i = 0; i < query.values.length; i++) {
            if (val[i] && val[i].val === query.values[i].formatted) {
              val[i].val = query.values[i].unformatted
            }
          }
        }

        let type = query.type
        //if the value is variable, omit quotes in the 'where' string
        const isValueVariable = query.valueVariable
        let variable = query.variable === null ? '' : query.variable
        let oper = query.operator === null ? '' : query.operator
        // let value = val === null ? "''" : val;
        let value
        value = this.checkOperator(oper, val, type).value
        if (typeof value === 'string' && value[0] !== '(') {
          value = value.replace("'", "''")
        }
        oper = this.checkOperator(oper, value, type).operator

        /* A temporal value is shown as a SAS literal (eg '20DEC2053'd) rather
           than the internal day / second count.  Only rewrite when the composed
           value is nothing but the numbers the clause is built from - a single
           value, a BETWEEN pair or an IN list.  The BETWEEN separator is
           removed before the test because ' AND ' is not part of that character
           set; a typed value that is not a number, such as a date entered by
           hand, would otherwise have every digit run inside it rewritten into
           an unrelated literal. */
        if (
          !isValueVariable &&
          ['DATE', 'DATETIME', 'TIME'].includes(query.ddtype) &&
          /^[-0-9.,()\s]+$/.test(String(value).replace(/\s+AND\s+/gi, ' '))
        ) {
          value = String(value).replace(
            /-?\d+(?:\.\d+)?/g,
            (match) =>
              this.helperService.sasTemporalLiteral(match, query.ddtype) ??
              match
          )
        }

        if (type === 'char' && oper !== 'IN' && oper !== 'NOT IN') {
          if (typeof value === 'undefined') {
            value = ''
          }

          if (isValueVariable) {
            value = ' ' + value + ' ' //without quotes, with spaces
          } else {
            value = " '" + value + "' " //with quotes and spaces
          }

          string = string + ' ' + variable + ' ' + oper + value + opr
        } else {
          if (type === 'num' && typeof value === 'undefined') {
            value = '.'
          }

          if (typeof value === 'undefined') {
            value = ''
          } else {
            value = ' ' + value + ' '
          }
          string = string + ' ' + variable + ' ' + oper + value + opr
        }
      }
      clauseArr.push(string)
    }
    where = ''
    let groupOper
    for (let i = 0; i < clauseArr.length; i++) {
      if (i < clauseArr.length - 1) {
        groupOper = groupOperator
      } else {
        groupOper = ''
      }
      if (clauseArr.length === 1) {
        where = clauseArr[0]
      } else {
        where = where + ' (' + clauseArr[i] + ') ' + groupOper
      }
    }

    whereString = where.substr(1).slice(0, -1)
    where = 'WHERE' + where + ';'

    let queryClause = {
      whereClause: where,
      string: whereString,
      obj: clauses,
      libds: libds
    }
    this.query.next(queryClause)
    return { whereClause: where, string: whereString }
  }

  /**
   * This function creates Filter Query Table
   * @param filterQuery an object of type FilterQuery
   * @returns {QueryClause[]} array of QueryClause
   */
  public createFilterQueryTable(filterQuery: FilterQuery): QueryClause[] {
    const filterQueryClauseTable: QueryClause[] = []

    filterQuery.filterGroups.forEach(
      (filterGroup: FilterGroup, groupIndex: number) => {
        filterGroup.filterClauses.forEach((filterClause: FilterClause) => {
          /* A temporal value is sent as a SAS literal (eg '20APR2042'd) rather
             than the internal day / second count, so the resolved WHERE clause
             - and the FILTER_TEXT shown above the grid - carries a date. */
          const temporalValue = (value: any) =>
            filterClause.valueVariable
              ? null
              : this.helperService.sasTemporalLiteral(
                  value,
                  filterClause.ddtype
                )

          /* The range dropdown binds the formatted label, as the value list
             and the IN modal do, so each end of a BETWEEN is mapped back to
             its unformatted value before the literal is built.  This is the
             same lookup whereClauseCreator applies for the preview - without
             it the formatted string reaches sasTemporalLiteral, comes back
             null, and RAW_VALUE carries an unquoted date that the backend
             rejects on a numeric column. */
          const unformattedValue = (value: any) => {
            const match = (filterClause.values || []).find(
              (entry: any) => entry.formatted === value
            )
            return match ? match.unformatted : value
          }

          let rawValue = ''
          if (filterClause.operator === 'BETWEEN') {
            const from = unformattedValue(filterClause.value[0])
            const to = unformattedValue(filterClause.value[1])
            rawValue = `${temporalValue(from) ?? from} AND ${
              temporalValue(to) ?? to
            }`
          } else if (
            filterClause.operator === 'IN' ||
            filterClause.operator === 'NOT IN'
          ) {
            filterClause.value.forEach((element: any) => {
              if (element.checked) {
                /* Mapped like the BETWEEN ends - whereClauseCreator normally
                   rewrites element.val to the unformatted count before submit,
                   so this is a pass-through for the usual path and a guard for
                   any path that reaches here without the preview having run. */
                const value = unformattedValue(element.val)
                const literal = temporalValue(value)
                if (literal) {
                  rawValue += (rawValue.length > 0 ? ',' : '(') + literal
                } else if (
                  typeof value === 'string' &&
                  (!isSpecialMissing(value) || filterClause.type === 'char')
                ) {
                  if (rawValue.length > 0) {
                    rawValue += `,'${value.replace(/'/g, "''")}'`
                  } else {
                    rawValue = `('${value.replace(/'/g, "''")}'`
                  }
                } else {
                  if (rawValue.length > 0) {
                    rawValue += `,${value}`
                  } else {
                    rawValue = `(${value}`
                  }
                }
              }
            })
            if (rawValue.length > 0) {
              rawValue += ')'
            }
          } else if (filterClause.value === '') {
            if (filterClause.type === 'char') {
              rawValue = `' '`
            } else {
              rawValue = '.'
            }
          } else {
            const literal = temporalValue(filterClause.value)
            if (literal) {
              rawValue = literal
            } else if (
              filterClause.type === 'char' &&
              !filterClause.valueVariable
            ) {
              rawValue = `'${filterClause.value.replace(/'/g, "''")}'`
            }
          }
          filterQueryClauseTable.push({
            GROUP_LOGIC: filterQuery.groupLogic,
            SUBGROUP_LOGIC: filterGroup.clauseLogic || 'AND',
            SUBGROUP_ID: groupIndex,
            VARIABLE_NM: filterClause.variable,
            OPERATOR_NM: filterClause.operator,
            RAW_VALUE: rawValue || filterClause.value
          })
        })
      }
    )

    return filterQueryClauseTable
  }

  /**
   * This helper function initializes global filter when user load the filtered url
   * @param path it tells whether function is being called from viewer component or editor
   * @param cols array of columns metadata
   */
  public initializeGlobalFilterClause(page: string, cols: Col[]): void {
    const clauses: any = {
      queryObj: [],
      clauseLogic: [],
      groupLogic: get(globals, page).filter.query[0].GROUP_LOGIC
    }

    get(globals, page).filter.clauses = this.helperService.deepClone(clauses)
    get(globals, page).filter.groupLogic = get(
      globals,
      page
    ).filter.query[0].GROUP_LOGIC

    let subGroupLogic = ''
    let group: any = { elements: [] }
    let groupID = 0

    get(globals, page).filter.query.forEach((obj: any) => {
      const temp: any = {}
      for (let i = 0; i < cols.length; i++) {
        if (cols[i].NAME === obj.VARIABLE_NM) {
          subGroupLogic = obj.SUBGROUP_LOGIC
          temp['ddtype'] = cols[i].DDTYPE
          temp['type'] = cols[i].TYPE

          if (cols[i].TYPE === 'num') {
            temp['operators'] = globals.operators.numOperators
          } else {
            temp['operators'] = globals.operators.charOperators
          }

          temp['logic'] = null
          temp['operator'] = obj.OPERATOR_NM
          temp['variable'] = obj.VARIABLE_NM

          if (cols[i].TYPE === 'char') {
            if (obj.OPERATOR_NM === 'IN' || obj.OPERATOR_NM === 'NOT IN') {
              const newValueArray: { checked: boolean; val: any }[] = []
              const valueString = obj.RAW_VALUE.slice(1, -1)
              const valueArray = valueString.split("','")

              if (valueArray.length === 1) {
                newValueArray.push({
                  checked: true,
                  val: valueArray[0].slice(1, -1).replace(/''/g, "'")
                })
              } else {
                valueArray.forEach((element: string, index: number) => {
                  if (index === 0) {
                    newValueArray.push({
                      checked: true,
                      val: element.slice(1).replace(/''/g, "'")
                    })
                  } else if (index === valueArray.length - 1) {
                    newValueArray.push({
                      checked: true,
                      val: element.slice(0, -1).replace(/''/g, "'")
                    })
                  } else {
                    newValueArray.push({
                      checked: true,
                      val: element.replace(/''/g, "'")
                    })
                  }
                })
              }
              temp['value'] = this.helperService.deepClone(newValueArray)
            } else {
              temp['value'] = obj.RAW_VALUE.slice(1, -1).replace(/''/g, "'")
            }
          } else {
            if (obj.OPERATOR_NM === 'IN' || obj.OPERATOR_NM === 'NOT IN') {
              const newValueArray: { checked: boolean; val: any }[] = []
              const valueString = obj.RAW_VALUE.slice(1, -1)
              const valueArray = valueString.split(',')
              valueArray.forEach((element: string, index: number) => {
                newValueArray.push({
                  checked: true,
                  val: element
                })
              })
              temp['value'] = this.helperService.deepClone(newValueArray)
            } else if (obj.OPERATOR_NM === 'BETWEEN') {
              const valueArray = obj.RAW_VALUE.split(' AND ')
              temp['value'] = valueArray
            } else {
              temp['value'] = obj.RAW_VALUE
            }
          }

          temp['values'] = []
          if (groupID === obj.SUBGROUP_ID) {
            group.elements.push(this.helperService.deepClone(temp))
          } else {
            get(globals, page).filter.clauses.queryObj.push(
              this.helperService.deepClone(group)
            )
            get(globals, page).filter.clauses.queryObj[groupID].clauseLogic =
              subGroupLogic
            get(globals, page).filter.clauses.queryObj[groupID].invalidClause =
              false
            groupID++
            group.elements = []
            group.elements.push(this.helperService.deepClone(temp))
          }
          break
        }
      }
    })
    if (group.elements.length > 0) {
      get(globals, page).filter.clauses.queryObj.push(
        this.helperService.deepClone(group)
      )
      get(globals, page).filter.clauses.queryObj[groupID].clauseLogic =
        subGroupLogic
      get(globals, page).filter.clauses.queryObj[groupID].invalidClause = false
    }
    get(globals, page).filter.query = []
  }

  public async removeClause() {
    let removeFlag = true
    this.removeQuery.next(removeFlag)
    return removeFlag
  }

  public specConversion(spec: any) {
    let specObj: SpecObj = {}
    let namesArr = []
    namesArr = spec.map(function (item: any) {
      return item.NAME.toUpperCase()
    })
    if (spec instanceof Array) {
      for (let index = 0; index < namesArr.length; index++) {
        if (spec[index].TYPE === 1) {
          specObj[namesArr[index]] = {
            colType: 'num',
            colLength: spec[index].LENGTH
          }
        } else {
          specObj[namesArr[index]] = {
            colType: 'string',
            colLength: spec[index].LENGTH
          }
        }
      }
    }
    return specObj
  }

  setSubmitReady() {
    let ready = true
    this.setSubmit.next(ready)
  }

  parseFormats($dataFormats: $DataFormats) {
    const formats: {
      [key: string]: any
    } = {}
    const vars = $dataFormats.vars

    for (let col of Object.keys(vars)) {
      const type = vars[col].type
      const length = vars[col].length

      if (type === 'num') {
        formats[col] = 'best.'
      } else if (type === 'char') {
        formats[col] = `$char${length}.`
      }
    }

    return formats
  }
}

results matching ""

    No results matching ""