src/app/services/sas-store.service.ts
No results matching.
Properties |
|
Methods |
|
constructor(sasService: SasService, helperService: HelperService, loggerService: LoggerService)
|
||||||||||||
|
Defined in src/app/services/sas-store.service.ts:43
|
||||||||||||
|
Parameters :
|
| Public Async approveTable |
approveTable(tableData: any, tableName: string, program: string)
|
|
Defined in src/app/services/sas-store.service.ts:275
|
|
Returns :
unknown
|
| Public Async callService | |||||||||||||||
callService(tableData: Array
|
|||||||||||||||
|
Defined in src/app/services/sas-store.service.ts:61
|
|||||||||||||||
|
Wrapper for making request to service Should be removed, as it's redundant now TODO: Refactor to call editors/getdata directly
Parameters :
Returns :
unknown
|
| Public checkOperator |
checkOperator(operator: any, value: any, type: string)
|
|
Defined in src/app/services/sas-store.service.ts:417
|
|
Returns :
{ value: any; operator: any; }
|
| Public createFilterQueryTable | ||||||||
createFilterQueryTable(filterQuery: FilterQuery)
|
||||||||
|
Defined in src/app/services/sas-store.service.ts:680
|
||||||||
|
This function creates Filter Query Table
Parameters :
Returns :
QueryClause[]
array of QueryClause |
| Public Async getApprovals | ||||||||||||||||
getApprovals(tableData: any, tableName: string, program: string)
|
||||||||||||||||
|
Defined in src/app/services/sas-store.service.ts:130
|
||||||||||||||||
|
Sending request to 'approvers/getapprovals' to fetch approvals list
Parameters :
Returns :
unknown
HTTP Response |
| Public Async getChangeInfo | ||||||
getChangeInfo(tableId: any)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:360
|
||||||
|
Parameters :
Returns :
unknown
|
| Public Async getCols | ||||||
getCols(libds: string)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:313
|
||||||
|
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 :
Returns :
unknown
|
| Public Async getHistory |
getHistory(tableData: any, tableName: string, program: string)
|
|
Defined in src/app/services/sas-store.service.ts:286
|
|
Returns :
unknown
|
| Public Async getQueryValues | ||||||||||||||||||
getQueryValues(variable: string, dataset: string, filterQuery: any[], STARTROW?: number, ROWS?: number)
|
||||||||||||||||||
|
Defined in src/app/services/sas-store.service.ts:367
|
||||||||||||||||||
|
Parameters :
Returns :
unknown
|
| Public Async getSubmitts |
getSubmitts()
|
|
Defined in src/app/services/sas-store.service.ts:160
|
|
Returns :
unknown
All submits |
| Public Async getXLMapRules | ||||||
getXLMapRules(id: string)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:248
|
||||||
|
Parameters :
Returns :
unknown
|
| Public handoffSubmits | ||||||
handoffSubmits(fromsas: Array
|
||||||
|
Defined in src/app/services/sas-store.service.ts:169
|
||||||
|
Carries the fetched submit queue over to the details URL navigation, so the re-created submit list component does not fetch it again.
Parameters :
Returns :
void
|
| Public initializeGlobalFilterClause |
initializeGlobalFilterClause(page: string, cols: Col[])
|
|
Defined in src/app/services/sas-store.service.ts:790
|
|
This helper function initializes global filter when user load the filtered url
Returns :
void
|
| Public Async openTable | ||||||
openTable(tableId: string)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:411
|
||||||
|
Parameters :
Returns :
unknown
|
| parseFormats | ||||||
parseFormats($dataFormats: $DataFormats)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:945
|
||||||
|
Parameters :
Returns :
{ [key: string]: any; }
|
| Public Async refreshLibInfo | ||||||
refreshLibInfo(libref: string)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:202
|
||||||
|
Parameters :
Returns :
unknown
|
| Public Async rejecting |
rejecting(tableData: any, tableName: string, program: string)
|
|
Defined in src/app/services/sas-store.service.ts:265
|
|
Returns :
unknown
|
| Public Async removeClause |
removeClause()
|
|
Defined in src/app/services/sas-store.service.ts:910
|
|
Returns :
unknown
|
| Public Async saveQuery | |||||||||
saveQuery(libds: string, filterQuery: QueryClause[])
|
|||||||||
|
Defined in src/app/services/sas-store.service.ts:398
|
|||||||||
|
Parameters :
Returns :
unknown
|
| Public Async sendDetails | ||||||||||||||||
sendDetails(detail: any, index: any, data: any)
|
||||||||||||||||
|
Defined in src/app/services/sas-store.service.ts:146
|
||||||||||||||||
|
Interceptor for loading of the submitted details
Parameters :
Returns :
any
|
| setQueryVariables |
setQueryVariables(dataset: string, cols: any)
|
|
Defined in src/app/services/sas-store.service.ts:292
|
|
Returns :
void
|
| Public Async setQueryVariablesFromTable | |||||||||
setQueryVariablesFromTable(libds: string, cols: any[])
|
|||||||||
|
Defined in src/app/services/sas-store.service.ts:339
|
|||||||||
|
Feeds the filter picker its variable list.
Parameters :
Returns :
any
|
| setSubmitReady |
setSubmitReady()
|
|
Defined in src/app/services/sas-store.service.ts:940
|
|
Returns :
void
|
| Public Async showDiffs |
showDiffs(tableData: any, tableName: string, program: string)
|
|
Defined in src/app/services/sas-store.service.ts:256
|
|
Returns :
unknown
|
| Public specConversion | ||||||
specConversion(spec: any)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:916
|
||||||
|
Parameters :
Returns :
SpecObj
|
| Public takeSubmitsHandoff |
takeSubmitsHandoff()
|
|
Defined in src/app/services/sas-store.service.ts:177
|
|
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)
|
||||||||||||||||||||||||||||||||
|
Defined in src/app/services/sas-store.service.ts:87
|
||||||||||||||||||||||||||||||||
|
Calling editors/stagedata - saving table data, sending request to backend
Parameters :
Returns :
Promise<RequestWrapperResponse<EditorsStageDataSASResponse>>
adapter.request() response |
| Public Async viewData |
viewData(libDataset: any, filter_pk: any)
|
|
Defined in src/app/services/sas-store.service.ts:217
|
|
Returns :
unknown
|
| Public Async viewDataSearch | ||||||||||||||||||||
viewDataSearch(searchVal: string, numeric: boolean, libDataset: any, filter_pk: any)
|
||||||||||||||||||||
|
Defined in src/app/services/sas-store.service.ts:225
|
||||||||||||||||||||
|
Parameters :
Returns :
unknown
|
| Public viewLibs |
viewLibs()
|
|
Defined in src/app/services/sas-store.service.ts:189
|
|
Returns :
any
All libraries |
| Public Async viewTables | ||||||
viewTables(lib: any)
|
||||||
|
Defined in src/app/services/sas-store.service.ts:211
|
||||||
|
Parameters :
Returns :
unknown
|
| Public whereClauseCreator |
whereClauseCreator(clauses: any, groupOperator: string, libds: string)
|
|
Defined in src/app/services/sas-store.service.ts:490
|
|
Returns :
{ whereClause: string; string: string; }
|
| Public changedTable |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:26
|
| Public columns |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:29
|
| Public details |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:27
|
| Public diffs |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:28
|
| Public filter |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:31
|
| Private libsPromise |
Type : Promise<any> | null
|
Default value : null
|
|
Defined in src/app/services/sas-store.service.ts:183
|
| Public query |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:32
|
| Public removeQuery |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:34
|
| Public response |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:25
|
| Public setSubmit |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:35
|
| Public setSubmitList |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:36
|
| Private submitsHandoff |
Type : Array<any> | null
|
Default value : null
|
|
Defined in src/app/services/sas-store.service.ts:43
|
|
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>()
|
|
Defined in src/app/services/sas-store.service.ts:33
|
| Public values |
Type : Subject<any>
|
Default value : new Subject<any>()
|
|
Defined in src/app/services/sas-store.service.ts:30
|
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
}
}