src/app/services/helper.service.ts
| providedIn | root |
Properties |
|
Methods |
|
constructor(sasService: SasService)
|
||||||
|
Defined in src/app/services/helper.service.ts:29
|
||||||
|
Parameters :
|
| Public addLeadingZero | ||||||
addLeadingZero(value: string | number)
|
||||||
|
Defined in src/app/services/helper.service.ts:309
|
||||||
|
Parameters :
Returns :
string
|
| Public convertArrayValues | |||||||||
convertArrayValues(array: any[], type: "string" | "number")
|
|||||||||
|
Defined in src/app/services/helper.service.ts:352
|
|||||||||
|
Parameters :
Returns :
[] | []
|
| Public convertJsDateToSasDate | |||||||||||||||
convertJsDateToSasDate(jsDate: string | Date, unit: string)
|
|||||||||||||||
|
Defined in src/app/services/helper.service.ts:44
|
|||||||||||||||
|
Converts a JavaScript date object to a SAS Date or Datetime, given the logic below: A JS Date contains the number of milliseconds since 01/01/1970 A SAS Date contains the number of days since 01/01/1960 A SAS Datetime contains the number of seconds since 01/01/1960
Parameters :
Returns :
number
SAS Date value based on |
| Public convertObjectsToArray | ||||||||||||
convertObjectsToArray(objectArray: Array
|
||||||||||||
|
Defined in src/app/services/helper.service.ts:230
|
||||||||||||
|
Parameters :
Returns :
any
|
| Public convertSasDaysToJsDate | |||||||||||||||
convertSasDaysToJsDate(sasValue: number | string, unit: string)
|
|||||||||||||||
|
Defined in src/app/services/helper.service.ts:101
|
|||||||||||||||
|
Converts a SAS Date or Datetime to a JavaScript date object, given the logic below: A JS Date contains the number of milliseconds since 01/01/1970 A SAS Date contains the number of days since 01/01/1960 A SAS Datetime contains the number of seconds since 01/01/1960
Parameters :
Returns :
any
JavaScript Date object |
| Public debounceCall |
debounceCall(time: number, callback: () => void)
|
|
Defined in src/app/services/helper.service.ts:385
|
|
Returns :
void
|
| Public deepClone | ||||||
deepClone(item: any)
|
||||||
|
Defined in src/app/services/helper.service.ts:391
|
||||||
|
Parameters :
Returns :
any
|
| Public deleteKeysAndHash | ||||||||||||||||||||
deleteKeysAndHash(data: any, keysToRemove: string[], clone: boolean)
|
||||||||||||||||||||
|
Defined in src/app/services/helper.service.ts:322
|
||||||||||||||||||||
|
Hashes the object, if array provided it first deletes the keys from object
Parameters :
Returns :
string
hashed string |
| Public displayLibraries |
displayLibraries(array: any, loadMore?: boolean)
|
|
Defined in src/app/services/helper.service.ts:181
|
|
Returns :
void
|
| downloadTextFile |
downloadTextFile(filename: string, text: string)
|
|
Defined in src/app/services/helper.service.ts:336
|
|
Returns :
void
|
| Public isStreamingViya |
isStreamingViya()
|
|
Defined in src/app/services/helper.service.ts:368
|
|
Returns :
boolean
|
| Public libraryOnFilter |
libraryOnFilter(array: any, search: string, fieldToMatch: any)
|
|
Defined in src/app/services/helper.service.ts:164
|
|
Returns :
void
|
| Public metaObjectOnFilter |
metaObjectOnFilter(array: any, search: string, fieldToMatch: any)
|
|
Defined in src/app/services/helper.service.ts:205
|
|
Returns :
void
|
| Public resetArrayFilter | ||||||
resetArrayFilter(array: any)
|
||||||
|
Defined in src/app/services/helper.service.ts:220
|
||||||
|
Parameters :
Returns :
void
|
| Public resetLibraryForeground | ||||||
resetLibraryForeground(array: any)
|
||||||
|
Defined in src/app/services/helper.service.ts:199
|
||||||
|
Parameters :
Returns :
void
|
| Private sasDateFromDays | ||||||
sasDateFromDays(days: number)
|
||||||
|
Defined in src/app/services/helper.service.ts:288
|
||||||
|
Parameters :
Returns :
string
|
| Public sasTemporalLiteral |
sasTemporalLiteral(value: any, ddtype: string)
|
|
Defined in src/app/services/helper.service.ts:265
|
|
Formats a SAS internal date / datetime / time value as the SAS literal a WHERE clause can carry, eg: 34322 (DATE) -> '20DEC2053'd 42 (DATETIME) -> '01JAN1960:00:00:42'dt 42 (TIME) -> '00:00:42't The value is days since 1960-01-01 for a date, and seconds since 1960-01-01 (datetime) or since midnight (time). The date part is built arithmetically in UTC, so the day cannot shift with the browser timezone. SAS temporal values are whole days and whole seconds, so the value is rounded to seconds first - that kills the noise a double picks up on the way here, where 1.9999999999999 is simply the second 2. A value that is genuinely fractional does not land on a second, and rounding it would silently widen the filter, so it returns null instead. Returns null when the value is not a finite number, is not a whole day or second, or the ddtype is not temporal - callers then send the raw value.
Returns :
string | null
|
| Private sasTimeFromSeconds | ||||||
sasTimeFromSeconds(seconds: number)
|
||||||
|
Defined in src/app/services/helper.service.ts:299
|
||||||
|
Parameters :
Returns :
string
|
| Public treeOnFilter | ||||||||||||
treeOnFilter(array: any, arrToFilter: string)
|
||||||||||||
|
Defined in src/app/services/helper.service.ts:130
|
||||||||||||
|
Parameters :
Returns :
void
|
| Private debounceTimeout |
Type : any
|
|
Defined in src/app/services/helper.service.ts:384
|
| Public loadMoreCount |
Type : number
|
Default value : librariesToShow
|
|
Defined in src/app/services/helper.service.ts:29
|
| Public shownLibraries |
Type : number
|
Default value : librariesToShow
|
|
Defined in src/app/services/helper.service.ts:28
|
import { Injectable } from '@angular/core'
import cloneDeep from 'lodash-es/cloneDeep'
import CryptoMD5 from 'crypto-js/md5'
import { SasService } from './sas.service'
const librariesToShow = 50
/** Month names used by a SAS date literal, eg 20APR2042. */
const sasMonths = [
'JAN',
'FEB',
'MAR',
'APR',
'MAY',
'JUN',
'JUL',
'AUG',
'SEP',
'OCT',
'NOV',
'DEC'
]
@Injectable({
providedIn: 'root'
})
export class HelperService {
public shownLibraries: number = librariesToShow
public loadMoreCount: number = librariesToShow
constructor(private sasService: SasService) {}
/**
* Converts a JavaScript date object to a SAS Date or Datetime, given the logic below:
*
* A JS Date contains the number of _milliseconds_ since 01/01/1970
* A SAS Date contains the number of _days_ since 01/01/1960
* A SAS Datetime contains the number of _seconds_ since 01/01/1960
*
* @param jsDate JS Date to be converted. The type is instance of `Date`
* @param unit Unit in which to return the SAS Date / datetime, eg `sasdate | sasdatetime`
* @returns SAS Date value based on `unit` param
*/
public convertJsDateToSasDate(
jsDate: string | Date,
unit: string = 'days'
): number {
let jsDateObject: Date
let valueInMilliseconds: number = 0
if (!(jsDate instanceof Date)) {
jsDateObject = new Date(jsDate)
} else {
jsDateObject = jsDate
}
valueInMilliseconds = new Date(
Date.UTC(
jsDateObject.getFullYear(),
jsDateObject.getMonth(),
jsDateObject.getDate(),
jsDateObject.getHours(),
jsDateObject.getMinutes(),
jsDateObject.getSeconds()
)
).valueOf()
const msInDay = 24 * 60 * 60 * 1000
const msInTenYears = 315619200000 //since calculating it is not practical to get correct value, this is parsed with: new Date(Date.UTC(1960, 0, 1)).getTime()
const sasMilliseconds = valueInMilliseconds + msInTenYears
switch (unit) {
case 'days': {
let valueInDays = sasMilliseconds / msInDay
valueInDays = Math.abs(valueInDays)
valueInDays = Math.floor(valueInDays)
return valueInDays
}
case 'seconds': {
return sasMilliseconds / 1000
}
}
return 0
}
/**
* Converts a SAS Date or Datetime to a JavaScript date object, given the logic below:
*
* A JS Date contains the number of _milliseconds_ since 01/01/1970
* A SAS Date contains the number of _days_ since 01/01/1960
* A SAS Datetime contains the number of _seconds_ since 01/01/1960
*
* @param sasValue SAS Date or Datetime to be converted. The type could be `number` or `string.
* @param unit Unit from which to convert the SAS Date / Datetime, eg `sasdate | sasdatetime`
* @returns JavaScript Date object
*/
public convertSasDaysToJsDate(
sasValue: number | string,
unit: string = 'days'
) {
const msInDay = 24 * 60 * 60 * 1000
const msNegativeTenYears = -315619200000 //since calculating it is not practical to get correct value, this is parsed with: new Date(Date.UTC(1960, 0, 1)).getTime()
if (typeof sasValue !== 'number') sasValue = parseFloat(sasValue)
if (unit === 'seconds') {
let sasMs = sasValue * 1000
let jsMs = msNegativeTenYears + sasMs
let timezoneOffsetMs = new Date(jsMs).getTimezoneOffset() * 60 * 1000
jsMs += timezoneOffsetMs
return new Date(jsMs)
}
return new Date(msNegativeTenYears + sasValue * msInDay)
}
/**
*
* @param array all elements in the clarity tree
* @param arrToFilter sub array in the tree to be filtered for example `tables`
*/
public treeOnFilter(array: any, arrToFilter: string) {
let search = array['searchString'] ? array['searchString'] : ''
let arrToFilterArray = arrToFilter.split('.')[0]
let arrToFilterField = arrToFilter.split('.')[1]
let arrToFilterAll = arrToFilterArray + 'All'
if (array[arrToFilterArray]) {
if (!array[arrToFilterAll]) {
array[arrToFilterAll] = this.deepClone(array[arrToFilterArray])
}
array[arrToFilterArray] = this.deepClone(array[arrToFilterAll])
if (search.length > 0) {
if (arrToFilterField) {
array[arrToFilterArray] = array[arrToFilterArray].filter(
(item: any) => {
return item[arrToFilterField]
.toLowerCase()
.includes(search.toLowerCase())
}
)
} else {
array[arrToFilterArray] = array[arrToFilterArray].filter(
(item: any) => {
return item.toLowerCase().includes(search.toLowerCase())
}
)
}
}
}
}
public libraryOnFilter(array: any, search: string, fieldToMatch: any) {
if (search.length > 0) {
array.forEach((item: any) => {
if (!item[fieldToMatch].toLowerCase().includes(search.toLowerCase())) {
item['hidden'] = true
item['inForeground'] = false
} else {
item['hidden'] = false
item['inForeground'] = true
}
})
} else {
this.resetArrayFilter(array)
this.displayLibraries(array)
}
}
public displayLibraries(array: any, loadMore?: boolean) {
if (loadMore) {
this.shownLibraries += this.loadMoreCount
} else {
this.shownLibraries = librariesToShow
this.resetLibraryForeground(array)
}
for (let index = 0; index < array.length; index++) {
if (index === this.shownLibraries) {
break
}
array[index]['inForeground'] = true
}
}
public resetLibraryForeground(array: any) {
for (let index = 0; index < array.length; index++) {
array[index]['inForeground'] = false
}
}
public metaObjectOnFilter(array: any, search: string, fieldToMatch: any) {
// array = this.deepClone(arrayAll);
if (search.length > 0) {
this.resetArrayFilter(array)
array.forEach((item: any) => {
if (!item[fieldToMatch].toLowerCase().includes(search.toLowerCase())) {
item['hidden'] = true
}
})
} else {
this.resetArrayFilter(array)
}
}
public resetArrayFilter(array: any) {
array.forEach((item: any) => {
item['hidden'] = false
if (item['inForeground']) {
delete item['inForeground']
}
})
}
public convertObjectsToArray(
objectArray: Array<object>,
deepClone: boolean = false
) {
if (deepClone) {
objectArray = this.deepClone(objectArray)
}
return objectArray.map((obj: any) => {
return Object.keys(obj).map((key) => {
return obj[key]
})
})
}
/**
* Formats a SAS internal date / datetime / time value as the SAS literal a
* WHERE clause can carry, eg:
* 34322 (DATE) -> '20DEC2053'd
* 42 (DATETIME) -> '01JAN1960:00:00:42'dt
* 42 (TIME) -> '00:00:42't
*
* The value is days since 1960-01-01 for a date, and seconds since
* 1960-01-01 (datetime) or since midnight (time). The date part is built
* arithmetically in UTC, so the day cannot shift with the browser timezone.
*
* SAS temporal values are whole days and whole seconds, so the value is
* rounded to seconds first - that kills the noise a double picks up on the
* way here, where 1.9999999999999 is simply the second 2. A value that is
* genuinely fractional does not land on a second, and rounding it would
* silently widen the filter, so it returns null instead.
*
* Returns null when the value is not a finite number, is not a whole day or
* second, or the ddtype is not temporal - callers then send the raw value.
*/
public sasTemporalLiteral(value: any, ddtype: string): string | null {
const kind = (ddtype ?? '').toString().toUpperCase()
if (kind !== 'DATE' && kind !== 'DATETIME' && kind !== 'TIME') return null
if (value === null || value === undefined || value === '') return null
const num = typeof value === 'number' ? value : Number(String(value).trim())
if (!isFinite(num)) return null
/* Within the precision of a double, is this a whole second? */
const seconds = Math.round(num * 1e8) / 1e8
if (!Number.isInteger(seconds)) return null
if (kind === 'TIME') return `'${this.sasTimeFromSeconds(seconds)}'t`
const days = kind === 'DATETIME' ? Math.floor(seconds / 86400) : seconds
const datePart = this.sasDateFromDays(days)
if (kind === 'DATE') return `'${datePart}'d`
return `'${datePart}:${this.sasTimeFromSeconds(seconds - days * 86400)}'dt`
}
/** `ddMMMyyyy` for a SAS date value (days since 1960-01-01). */
private sasDateFromDays(days: number): string {
const date = new Date(Date.UTC(1960, 0, 1) + days * 86400000)
return (
this.addLeadingZero(date.getUTCDate()) +
sasMonths[date.getUTCMonth()] +
date.getUTCFullYear()
)
}
/** `hh:mm:ss` for a SAS time value (whole seconds since midnight). */
private sasTimeFromSeconds(seconds: number): string {
return (
this.addLeadingZero(Math.floor(seconds / 3600)) +
':' +
this.addLeadingZero(Math.floor((seconds % 3600) / 60)) +
':' +
this.addLeadingZero(seconds % 60)
)
}
public addLeadingZero(value: string | number): string {
if (typeof value !== 'string') value = value.toString()
return value.length < 2 ? '0' + value : value
}
/**
* Hashes the object, if array provided it first deletes the keys from object
* @param data data to hash, it will be deep cloned
* @param keysToRemove keys to remove from data object
* @param clone selecting whether to deep clone data or not. Default is true
* @returns hashed string
*/
public deleteKeysAndHash(
data: any,
keysToRemove: string[],
clone: boolean = true
): string {
const dataToHash = clone ? this.deepClone(data) : data
for (let key of keysToRemove) {
delete dataToHash[key]
}
return CryptoMD5(JSON.stringify(dataToHash)).toString()
}
downloadTextFile(filename: string, text: string) {
const element = document.createElement('a')
element.setAttribute(
'href',
'data:text/plain;charset=utf-8,' + encodeURIComponent(text)
)
element.setAttribute('download', filename + '.txt')
element.style.display = 'none'
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
}
public convertArrayValues(
array: any[],
type: 'string' | 'number'
): string[] | number[] {
if (array.length < 1) return []
switch (type) {
case 'number': {
return array.map((value) => value * 1)
}
case 'string': {
return array.map((value) => `${value}`)
}
}
}
public isStreamingViya(): boolean {
const serverType = this.sasService.getServerType()
if (serverType !== 'SASVIYA') return false
if (
location.search.toLowerCase().includes('?_file=') &&
location.pathname.toLowerCase().includes('/sasjobexecution')
)
return true
return false
}
// Required type is NodeJS.Timeout
// But NodeJS is not available in browser so we have to go with any
private debounceTimeout: any
public debounceCall(time: number, callback: () => void) {
clearTimeout(this.debounceTimeout)
this.debounceTimeout = setTimeout(callback, time)
}
public deepClone(item: any) {
return cloneDeep(item)
}
}