mirror of
https://github.com/sasjs/adapter.git
synced 2025-12-10 17:04:36 +00:00
chore(*): change code style to use single quote
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": false
|
||||
"singleQuote": true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isLogInRequired, needsRetry } from "./utils"
|
||||
import { CsrfToken } from "./types/CsrfToken"
|
||||
import { UploadFile } from "./types/UploadFile"
|
||||
import { isLogInRequired, needsRetry } from './utils'
|
||||
import { CsrfToken } from './types/CsrfToken'
|
||||
import { UploadFile } from './types/UploadFile'
|
||||
|
||||
const requestRetryLimit = 5
|
||||
|
||||
@@ -15,9 +15,9 @@ export class FileUploader {
|
||||
private retryCount = 0
|
||||
|
||||
public uploadFile(sasJob: string, files: UploadFile[], params: any) {
|
||||
if (files?.length < 1) throw new Error("Atleast one file must be provided")
|
||||
if (files?.length < 1) throw new Error('Atleast one file must be provided')
|
||||
|
||||
let paramsString = ""
|
||||
let paramsString = ''
|
||||
|
||||
for (let param in params) {
|
||||
if (params.hasOwnProperty(param)) {
|
||||
@@ -26,41 +26,41 @@ export class FileUploader {
|
||||
}
|
||||
|
||||
const program = this.appLoc
|
||||
? this.appLoc.replace(/\/?$/, "/") + sasJob.replace(/^\//, "")
|
||||
? this.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
|
||||
: sasJob
|
||||
const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
|
||||
"_program=" + program
|
||||
'_program=' + program
|
||||
}${paramsString}`
|
||||
|
||||
const headers = {
|
||||
"cache-control": "no-cache"
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const formData = new FormData()
|
||||
|
||||
for (let file of files) {
|
||||
formData.append("file", file.file, file.fileName)
|
||||
formData.append('file', file.file, file.fileName)
|
||||
}
|
||||
|
||||
if (this.csrfToken) formData.append("_csrf", this.csrfToken.value)
|
||||
if (this.csrfToken) formData.append('_csrf', this.csrfToken.value)
|
||||
|
||||
fetch(uploadUrl, {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
referrerPolicy: "same-origin",
|
||||
referrerPolicy: 'same-origin',
|
||||
headers
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 403) {
|
||||
const tokenHeader = response.headers.get("X-CSRF-HEADER")
|
||||
const tokenHeader = response.headers.get('X-CSRF-HEADER')
|
||||
|
||||
if (tokenHeader) {
|
||||
const token = response.headers.get(tokenHeader)
|
||||
this.csrfToken = {
|
||||
headerName: tokenHeader,
|
||||
value: token || ""
|
||||
value: token || ''
|
||||
}
|
||||
|
||||
this.setCsrfTokenWeb(this.csrfToken)
|
||||
@@ -72,7 +72,7 @@ export class FileUploader {
|
||||
})
|
||||
.then((responseText) => {
|
||||
if (isLogInRequired(responseText))
|
||||
reject("You must be logged in to upload a fle")
|
||||
reject('You must be logged in to upload a fle')
|
||||
|
||||
if (needsRetry(responseText)) {
|
||||
if (this.retryCount < requestRetryLimit) {
|
||||
|
||||
@@ -33,11 +33,11 @@ export class SAS9ApiClient {
|
||||
serverName: string,
|
||||
repositoryName: string
|
||||
) {
|
||||
const requestPayload = linesOfCode.join("\n")
|
||||
const requestPayload = linesOfCode.join('\n')
|
||||
const executeScriptRequest = {
|
||||
method: "PUT",
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: "application/json"
|
||||
Accept: 'application/json'
|
||||
},
|
||||
body: `command=${requestPayload}`
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ import {
|
||||
parseAndSubmitAuthorizeForm,
|
||||
convertToCSV,
|
||||
makeRequest
|
||||
} from "./utils"
|
||||
import * as NodeFormData from "form-data"
|
||||
import * as path from "path"
|
||||
import { Job, Session, Context, Folder, CsrfToken } from "./types"
|
||||
import { JobDefinition } from "./types/JobDefinition"
|
||||
import { formatDataForRequest } from "./utils/formatDataForRequest"
|
||||
import { SessionManager } from "./SessionManager"
|
||||
} from './utils'
|
||||
import * as NodeFormData from 'form-data'
|
||||
import * as path from 'path'
|
||||
import { Job, Session, Context, Folder, CsrfToken } from './types'
|
||||
import { JobDefinition } from './types/JobDefinition'
|
||||
import { formatDataForRequest } from './utils/formatDataForRequest'
|
||||
import { SessionManager } from './SessionManager'
|
||||
|
||||
/**
|
||||
* A client for interfacing with the SAS Viya REST API
|
||||
@@ -24,7 +24,7 @@ export class SASViyaApiClient {
|
||||
private rootFolderMap = new Map<string, Job[]>()
|
||||
) {
|
||||
if (!rootFolderName) {
|
||||
throw new Error("Root folder must be provided.")
|
||||
throw new Error('Root folder must be provided.')
|
||||
}
|
||||
}
|
||||
private csrfToken: CsrfToken | null = null
|
||||
@@ -73,7 +73,7 @@ export class SASViyaApiClient {
|
||||
*/
|
||||
public async getAllContexts(accessToken?: string) {
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
@@ -98,7 +98,7 @@ export class SASViyaApiClient {
|
||||
*/
|
||||
public async getExecutableContexts(accessToken?: string) {
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
@@ -112,7 +112,7 @@ export class SASViyaApiClient {
|
||||
const executableContexts: any[] = []
|
||||
|
||||
const promises = contextsList.map((context: any) => {
|
||||
const linesOfCode = ["%put &=sysuserid;"]
|
||||
const linesOfCode = ['%put &=sysuserid;']
|
||||
return this.executeScript(
|
||||
`test-${context.name}`,
|
||||
linesOfCode,
|
||||
@@ -122,14 +122,14 @@ export class SASViyaApiClient {
|
||||
})
|
||||
const results = await Promise.all(promises)
|
||||
results.forEach((result: any, index: number) => {
|
||||
if (result && result.jobStatus === "completed") {
|
||||
let sysUserId = ""
|
||||
if (result && result.jobStatus === 'completed') {
|
||||
let sysUserId = ''
|
||||
if (result && result.log && result.log.items) {
|
||||
const sysUserIdLog = result.log.items.find((i: any) =>
|
||||
i.line.startsWith("SYSUSERID=")
|
||||
i.line.startsWith('SYSUSERID=')
|
||||
)
|
||||
if (sysUserIdLog) {
|
||||
sysUserId = sysUserIdLog.line.replace("SYSUSERID=", "")
|
||||
sysUserId = sysUserIdLog.line.replace('SYSUSERID=', '')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ export class SASViyaApiClient {
|
||||
*/
|
||||
public async createSession(contextName: string, accessToken?: string) {
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
if (accessToken) {
|
||||
@@ -175,10 +175,10 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
const createSessionRequest = {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
const { result: createdSession } = await this.request<Session>(
|
||||
@@ -210,7 +210,7 @@ export class SASViyaApiClient {
|
||||
silent = !debug
|
||||
try {
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
if (accessToken) {
|
||||
@@ -231,26 +231,26 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
jobArguments["_OMITTEXTLOG"] = false
|
||||
jobArguments["_OMITSESSIONRESULTS"] = false
|
||||
jobArguments["_DEBUG"] = 131
|
||||
jobArguments['_OMITTEXTLOG'] = false
|
||||
jobArguments['_OMITSESSIONRESULTS'] = false
|
||||
jobArguments['_DEBUG'] = 131
|
||||
}
|
||||
|
||||
const fileName = `exec-${
|
||||
jobName.includes("/") ? jobName.split("/")[1] : jobName
|
||||
jobName.includes('/') ? jobName.split('/')[1] : jobName
|
||||
}`
|
||||
|
||||
let jobVariables: any = {
|
||||
SYS_JES_JOB_URI: "",
|
||||
_program: this.rootFolderName + "/" + jobName
|
||||
SYS_JES_JOB_URI: '',
|
||||
_program: this.rootFolderName + '/' + jobName
|
||||
}
|
||||
|
||||
let files: any[] = []
|
||||
|
||||
if (data) {
|
||||
if (JSON.stringify(data).includes(";")) {
|
||||
if (JSON.stringify(data).includes(';')) {
|
||||
files = await this.uploadTables(data, accessToken)
|
||||
jobVariables["_webin_file_count"] = files.length
|
||||
jobVariables['_webin_file_count'] = files.length
|
||||
files.forEach((fileInfo, index) => {
|
||||
jobVariables[
|
||||
`_webin_fileuri${index + 1}`
|
||||
@@ -264,11 +264,11 @@ export class SASViyaApiClient {
|
||||
|
||||
// Execute job in session
|
||||
const postJobRequest = {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
name: fileName,
|
||||
description: "Powered by SASjs",
|
||||
description: 'Powered by SASjs',
|
||||
code: linesOfCode,
|
||||
variables: jobVariables,
|
||||
arguments: jobArguments
|
||||
@@ -284,7 +284,7 @@ export class SASViyaApiClient {
|
||||
console.log(`Job has been submitted for ${fileName}`)
|
||||
console.log(
|
||||
`You can monitor the job progress at ${this.serverUrl}${
|
||||
postedJob.links.find((l: any) => l.rel === "state")!.href
|
||||
postedJob.links.find((l: any) => l.rel === 'state')!.href
|
||||
}`
|
||||
)
|
||||
}
|
||||
@@ -303,7 +303,7 @@ export class SASViyaApiClient {
|
||||
|
||||
let jobResult, log
|
||||
|
||||
const logLink = currentJob.links.find((l) => l.rel === "log")
|
||||
const logLink = currentJob.links.find((l) => l.rel === 'log')
|
||||
|
||||
if (true && logLink) {
|
||||
log = await this.request<any>(
|
||||
@@ -312,11 +312,11 @@ export class SASViyaApiClient {
|
||||
headers
|
||||
}
|
||||
).then((res: any) =>
|
||||
res.result.items.map((i: any) => i.line).join("\n")
|
||||
res.result.items.map((i: any) => i.line).join('\n')
|
||||
)
|
||||
}
|
||||
|
||||
if (jobStatus === "failed" || jobStatus === "error") {
|
||||
if (jobStatus === 'failed' || jobStatus === 'error') {
|
||||
return Promise.reject({ error: currentJob.error, log: log })
|
||||
}
|
||||
const resultLink = `/compute/sessions/${executionSessionId}/filerefs/_webout/content`
|
||||
@@ -325,7 +325,7 @@ export class SASViyaApiClient {
|
||||
jobResult = await this.request<any>(
|
||||
`${this.serverUrl}${resultLink}`,
|
||||
{ headers },
|
||||
"text"
|
||||
'text'
|
||||
).catch((e) => ({
|
||||
result: JSON.stringify(e)
|
||||
}))
|
||||
@@ -367,7 +367,7 @@ export class SASViyaApiClient {
|
||||
accessToken?: string
|
||||
): Promise<Folder> {
|
||||
if (!parentFolderPath && !parentFolderUri) {
|
||||
throw new Error("Parent folder path or uri is required")
|
||||
throw new Error('Parent folder path or uri is required')
|
||||
}
|
||||
|
||||
if (!parentFolderUri && parentFolderPath) {
|
||||
@@ -377,11 +377,11 @@ export class SASViyaApiClient {
|
||||
|
||||
const newParentFolderPath = parentFolderPath.substring(
|
||||
0,
|
||||
parentFolderPath.lastIndexOf("/")
|
||||
parentFolderPath.lastIndexOf('/')
|
||||
)
|
||||
const newFolderName = `${parentFolderPath.split("/").pop()}`
|
||||
if (newParentFolderPath === "") {
|
||||
throw new Error("Root Folder should have been present on server")
|
||||
const newFolderName = `${parentFolderPath.split('/').pop()}`
|
||||
if (newParentFolderPath === '') {
|
||||
throw new Error('Root Folder should have been present on server')
|
||||
}
|
||||
console.log(
|
||||
`Creating Parent Folder:\n${newFolderName} in ${newParentFolderPath}`
|
||||
@@ -398,14 +398,14 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
const createFolderRequest: RequestInit = {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: folderName,
|
||||
type: "folder"
|
||||
type: 'folder'
|
||||
})
|
||||
}
|
||||
|
||||
createFolderRequest.headers = { "Content-Type": "application/json" }
|
||||
createFolderRequest.headers = { 'Content-Type': 'application/json' }
|
||||
if (accessToken) {
|
||||
createFolderRequest.headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
@@ -437,7 +437,7 @@ export class SASViyaApiClient {
|
||||
) {
|
||||
if (!parentFolderPath && !parentFolderUri) {
|
||||
throw new Error(
|
||||
"Either parentFolderPath or parentFolderUri must be provided"
|
||||
'Either parentFolderPath or parentFolderUri must be provided'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -446,21 +446,21 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
const createJobDefinitionRequest: RequestInit = {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/vnd.sas.job.definition+json",
|
||||
Accept: "application/vnd.sas.job.definition+json"
|
||||
'Content-Type': 'application/vnd.sas.job.definition+json',
|
||||
Accept: 'application/vnd.sas.job.definition+json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: jobName,
|
||||
parameters: [
|
||||
{
|
||||
name: "_addjesbeginendmacros",
|
||||
type: "CHARACTER",
|
||||
defaultValue: "false"
|
||||
name: '_addjesbeginendmacros',
|
||||
type: 'CHARACTER',
|
||||
defaultValue: 'false'
|
||||
}
|
||||
],
|
||||
type: "Compute",
|
||||
type: 'Compute',
|
||||
code
|
||||
})
|
||||
}
|
||||
@@ -486,12 +486,12 @@ export class SASViyaApiClient {
|
||||
const authUrl = `${this.serverUrl}/SASLogon/oauth/authorize?client_id=${clientId}&response_type=code`
|
||||
|
||||
const authCode = await fetch(authUrl, {
|
||||
referrerPolicy: "same-origin",
|
||||
credentials: "include"
|
||||
referrerPolicy: 'same-origin',
|
||||
credentials: 'include'
|
||||
})
|
||||
.then((response) => response.text())
|
||||
.then(async (response) => {
|
||||
let code = ""
|
||||
let code = ''
|
||||
if (isAuthorizeFormRequired(response)) {
|
||||
const formResponse: any = await parseAndSubmitAuthorizeForm(
|
||||
response,
|
||||
@@ -499,21 +499,21 @@ export class SASViyaApiClient {
|
||||
)
|
||||
|
||||
const responseBody = formResponse
|
||||
.split("<body>")[1]
|
||||
.split("</body>")[0]
|
||||
const bodyElement: any = document.createElement("div")
|
||||
.split('<body>')[1]
|
||||
.split('</body>')[0]
|
||||
const bodyElement: any = document.createElement('div')
|
||||
bodyElement.innerHTML = responseBody
|
||||
|
||||
code = bodyElement.querySelector(".infobox h4").innerText
|
||||
code = bodyElement.querySelector('.infobox h4').innerText
|
||||
|
||||
return code
|
||||
} else {
|
||||
const responseBody = response.split("<body>")[1].split("</body>")[0]
|
||||
const bodyElement: any = document.createElement("div")
|
||||
const responseBody = response.split('<body>')[1].split('</body>')[0]
|
||||
const bodyElement: any = document.createElement('div')
|
||||
bodyElement.innerHTML = responseBody
|
||||
|
||||
if (bodyElement) {
|
||||
code = bodyElement.querySelector(".infobox h4").innerText
|
||||
code = bodyElement.querySelector('.infobox h4').innerText
|
||||
}
|
||||
|
||||
return code
|
||||
@@ -535,34 +535,34 @@ export class SASViyaApiClient {
|
||||
clientSecret: string,
|
||||
authCode: string
|
||||
) {
|
||||
const url = this.serverUrl + "/SASLogon/oauth/token"
|
||||
const url = this.serverUrl + '/SASLogon/oauth/token'
|
||||
let token
|
||||
if (typeof Buffer === "undefined") {
|
||||
token = btoa(clientId + ":" + clientSecret)
|
||||
if (typeof Buffer === 'undefined') {
|
||||
token = btoa(clientId + ':' + clientSecret)
|
||||
} else {
|
||||
token = Buffer.from(clientId + ":" + clientSecret).toString("base64")
|
||||
token = Buffer.from(clientId + ':' + clientSecret).toString('base64')
|
||||
}
|
||||
const headers = {
|
||||
Authorization: "Basic " + token
|
||||
Authorization: 'Basic ' + token
|
||||
}
|
||||
|
||||
let formData
|
||||
if (typeof FormData === "undefined") {
|
||||
if (typeof FormData === 'undefined') {
|
||||
formData = new NodeFormData()
|
||||
formData.append("grant_type", "authorization_code")
|
||||
formData.append("code", authCode)
|
||||
formData.append('grant_type', 'authorization_code')
|
||||
formData.append('code', authCode)
|
||||
} else {
|
||||
formData = new FormData()
|
||||
formData.append("grant_type", "authorization_code")
|
||||
formData.append("code", authCode)
|
||||
formData.append('grant_type', 'authorization_code')
|
||||
formData.append('code', authCode)
|
||||
}
|
||||
|
||||
const authResponse = await fetch(url, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers,
|
||||
body: formData as any,
|
||||
referrerPolicy: "same-origin"
|
||||
referrerPolicy: 'same-origin'
|
||||
}).then((res) => res.json())
|
||||
|
||||
return authResponse
|
||||
@@ -579,34 +579,34 @@ export class SASViyaApiClient {
|
||||
clientSecret: string,
|
||||
refreshToken: string
|
||||
) {
|
||||
const url = this.serverUrl + "/SASLogon/oauth/token"
|
||||
const url = this.serverUrl + '/SASLogon/oauth/token'
|
||||
let token
|
||||
if (typeof Buffer === "undefined") {
|
||||
token = btoa(clientId + ":" + clientSecret)
|
||||
if (typeof Buffer === 'undefined') {
|
||||
token = btoa(clientId + ':' + clientSecret)
|
||||
} else {
|
||||
token = Buffer.from(clientId + ":" + clientSecret).toString("base64")
|
||||
token = Buffer.from(clientId + ':' + clientSecret).toString('base64')
|
||||
}
|
||||
const headers = {
|
||||
Authorization: "Basic " + token
|
||||
Authorization: 'Basic ' + token
|
||||
}
|
||||
|
||||
let formData
|
||||
if (typeof FormData === "undefined") {
|
||||
if (typeof FormData === 'undefined') {
|
||||
formData = new NodeFormData()
|
||||
formData.append("grant_type", "refresh_token")
|
||||
formData.append("refresh_token", refreshToken)
|
||||
formData.append('grant_type', 'refresh_token')
|
||||
formData.append('refresh_token', refreshToken)
|
||||
} else {
|
||||
formData = new FormData()
|
||||
formData.append("grant_type", "refresh_token")
|
||||
formData.append("refresh_token", refreshToken)
|
||||
formData.append('grant_type', 'refresh_token')
|
||||
formData.append('refresh_token', refreshToken)
|
||||
}
|
||||
|
||||
const authResponse = await fetch(url, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers,
|
||||
body: formData as any,
|
||||
referrerPolicy: "same-origin"
|
||||
referrerPolicy: 'same-origin'
|
||||
}).then((res) => res.json())
|
||||
|
||||
return authResponse
|
||||
@@ -624,8 +624,8 @@ export class SASViyaApiClient {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
const deleteResponse = await this.request(url, {
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
method: 'DELETE',
|
||||
credentials: 'include',
|
||||
headers
|
||||
})
|
||||
|
||||
@@ -651,8 +651,8 @@ export class SASViyaApiClient {
|
||||
await this.populateRootFolder(accessToken)
|
||||
}
|
||||
if (!this.rootFolder) {
|
||||
console.error("Root folder was not found")
|
||||
throw new Error("Root folder was not found")
|
||||
console.error('Root folder was not found')
|
||||
throw new Error('Root folder was not found')
|
||||
}
|
||||
if (!this.rootFolderMap.size) {
|
||||
await this.populateRootFolderMap(accessToken)
|
||||
@@ -664,27 +664,27 @@ export class SASViyaApiClient {
|
||||
)
|
||||
}
|
||||
|
||||
const headers: any = { "Content-Type": "application/json" }
|
||||
const headers: any = { 'Content-Type': 'application/json' }
|
||||
if (!!accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
|
||||
const folderName = sasJob.split("/")[0]
|
||||
const jobName = sasJob.split("/")[1]
|
||||
const folderName = sasJob.split('/')[0]
|
||||
const jobName = sasJob.split('/')[1]
|
||||
const jobFolder = this.rootFolderMap.get(folderName)
|
||||
const jobToExecute = jobFolder?.find((item) => item.name === jobName)
|
||||
if (!jobToExecute) {
|
||||
throw new Error("Job was not found.")
|
||||
throw new Error('Job was not found.')
|
||||
}
|
||||
|
||||
let code = jobToExecute?.code
|
||||
if (!code) {
|
||||
const jobDefinitionLink = jobToExecute?.links.find(
|
||||
(l) => l.rel === "getResource"
|
||||
(l) => l.rel === 'getResource'
|
||||
)
|
||||
if (!jobDefinitionLink) {
|
||||
console.error("Job definition URI was not found.")
|
||||
throw new Error("Job definition URI was not found.")
|
||||
console.error('Job definition URI was not found.')
|
||||
throw new Error('Job definition URI was not found.')
|
||||
}
|
||||
const { result: jobDefinition } = await this.request<JobDefinition>(
|
||||
`${this.serverUrl}${jobDefinitionLink.href}`,
|
||||
@@ -696,7 +696,7 @@ export class SASViyaApiClient {
|
||||
// Add code to existing job definition
|
||||
jobToExecute.code = code
|
||||
}
|
||||
const linesToExecute = code.replace(/\r\n/g, "\n").split("\n")
|
||||
const linesToExecute = code.replace(/\r\n/g, '\n').split('\n')
|
||||
return await this.executeScript(
|
||||
sasJob,
|
||||
linesToExecute,
|
||||
@@ -728,7 +728,7 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
if (!this.rootFolder) {
|
||||
throw new Error("Root folder was not found")
|
||||
throw new Error('Root folder was not found')
|
||||
}
|
||||
if (!this.rootFolderMap.size) {
|
||||
await this.populateRootFolderMap(accessToken)
|
||||
@@ -745,17 +745,17 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
const jobName = path.basename(sasJob)
|
||||
const jobFolder = sasJob.replace(`/${jobName}`, "")
|
||||
const allJobsInFolder = this.rootFolderMap.get(jobFolder.replace("/", ""))
|
||||
const jobFolder = sasJob.replace(`/${jobName}`, '')
|
||||
const allJobsInFolder = this.rootFolderMap.get(jobFolder.replace('/', ''))
|
||||
if (allJobsInFolder) {
|
||||
const jobSpec = allJobsInFolder.find((j: Job) => j.name === jobName)
|
||||
const jobDefinitionLink = jobSpec?.links.find(
|
||||
(l) => l.rel === "getResource"
|
||||
(l) => l.rel === 'getResource'
|
||||
)?.href
|
||||
const requestInfo: any = {
|
||||
method: "GET"
|
||||
method: 'GET'
|
||||
}
|
||||
const headers: any = { "Content-Type": "application/json" }
|
||||
const headers: any = { 'Content-Type': 'application/json' }
|
||||
if (!!accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
@@ -777,9 +777,9 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
jobArguments["_OMITTEXTLOG"] = "false"
|
||||
jobArguments["_OMITSESSIONRESULTS"] = "false"
|
||||
jobArguments["_DEBUG"] = 131
|
||||
jobArguments['_OMITTEXTLOG'] = 'false'
|
||||
jobArguments['_OMITSESSIONRESULTS'] = 'false'
|
||||
jobArguments['_DEBUG'] = 131
|
||||
}
|
||||
|
||||
files.forEach((fileInfo, index) => {
|
||||
@@ -790,11 +790,11 @@ export class SASViyaApiClient {
|
||||
})
|
||||
|
||||
const postJobRequest = {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
name: `exec-${jobName}`,
|
||||
description: "Powered by SASjs",
|
||||
description: 'Powered by SASjs',
|
||||
jobDefinition,
|
||||
arguments: jobArguments
|
||||
})
|
||||
@@ -815,16 +815,16 @@ export class SASViyaApiClient {
|
||||
)
|
||||
|
||||
let jobResult, log
|
||||
if (jobStatus === "failed") {
|
||||
if (jobStatus === 'failed') {
|
||||
return Promise.reject(currentJob.error)
|
||||
}
|
||||
const resultLink = currentJob.results["_webout.json"]
|
||||
const logLink = currentJob.links.find((l) => l.rel === "log")
|
||||
const resultLink = currentJob.results['_webout.json']
|
||||
const logLink = currentJob.links.find((l) => l.rel === 'log')
|
||||
if (resultLink) {
|
||||
jobResult = await this.request<any>(
|
||||
`${this.serverUrl}${resultLink}/content`,
|
||||
{ headers },
|
||||
"text"
|
||||
'text'
|
||||
)
|
||||
}
|
||||
if (debug && logLink) {
|
||||
@@ -834,7 +834,7 @@ export class SASViyaApiClient {
|
||||
headers
|
||||
}
|
||||
).then((res: any) =>
|
||||
res.result.items.map((i: any) => i.line).join("\n")
|
||||
res.result.items.map((i: any) => i.line).join('\n')
|
||||
)
|
||||
}
|
||||
return { result: jobResult?.result, log }
|
||||
@@ -847,9 +847,9 @@ export class SASViyaApiClient {
|
||||
|
||||
private async populateRootFolderMap(accessToken?: string) {
|
||||
const allItems = new Map<string, Job[]>()
|
||||
const url = "/folders/folders/@item?path=" + this.rootFolderName
|
||||
const url = '/folders/folders/@item?path=' + this.rootFolderName
|
||||
const requestInfo: any = {
|
||||
method: "GET"
|
||||
method: 'GET'
|
||||
}
|
||||
if (accessToken) {
|
||||
requestInfo.headers = { Authorization: `Bearer ${accessToken}` }
|
||||
@@ -859,7 +859,7 @@ export class SASViyaApiClient {
|
||||
requestInfo
|
||||
)
|
||||
if (!folder) {
|
||||
throw new Error("Cannot populate RootFolderMap unless rootFolder exists")
|
||||
throw new Error('Cannot populate RootFolderMap unless rootFolder exists')
|
||||
}
|
||||
const { result: members } = await this.request<{ items: any[] }>(
|
||||
`${this.serverUrl}/folders/folders/${folder.id}/members`,
|
||||
@@ -867,14 +867,14 @@ export class SASViyaApiClient {
|
||||
)
|
||||
|
||||
const itemsAtRoot = members.items
|
||||
allItems.set("", itemsAtRoot)
|
||||
allItems.set('', itemsAtRoot)
|
||||
const subfolderRequests = members.items
|
||||
.filter((i: any) => i.contentType === "folder")
|
||||
.filter((i: any) => i.contentType === 'folder')
|
||||
.map(async (member: any) => {
|
||||
const subFolderUrl =
|
||||
"/folders/folders/@item?path=" +
|
||||
'/folders/folders/@item?path=' +
|
||||
this.rootFolderName +
|
||||
"/" +
|
||||
'/' +
|
||||
member.name
|
||||
const { result: memberDetail } = await this.request<Folder>(
|
||||
`${this.serverUrl}${subFolderUrl}`,
|
||||
@@ -882,7 +882,7 @@ export class SASViyaApiClient {
|
||||
)
|
||||
|
||||
const membersLink = memberDetail.links.find(
|
||||
(l: any) => l.rel === "members"
|
||||
(l: any) => l.rel === 'members'
|
||||
)
|
||||
|
||||
const { result: memberContents } = await this.request<{ items: any[] }>(
|
||||
@@ -899,9 +899,9 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
private async populateRootFolder(accessToken?: string) {
|
||||
const url = "/folders/folders/@item?path=" + this.rootFolderName
|
||||
const url = '/folders/folders/@item?path=' + this.rootFolderName
|
||||
const requestInfo: RequestInit = {
|
||||
method: "GET"
|
||||
method: 'GET'
|
||||
}
|
||||
if (accessToken) {
|
||||
requestInfo.headers = { Authorization: `Bearer ${accessToken}` }
|
||||
@@ -926,18 +926,18 @@ export class SASViyaApiClient {
|
||||
) {
|
||||
const MAX_POLL_COUNT = 1000
|
||||
const POLL_INTERVAL = 100
|
||||
let postedJobState = ""
|
||||
let postedJobState = ''
|
||||
let pollCount = 0
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json",
|
||||
"If-None-Match": etag
|
||||
'Content-Type': 'application/json',
|
||||
'If-None-Match': etag
|
||||
}
|
||||
if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
const stateLink = postedJob.links.find((l: any) => l.rel === "state")
|
||||
const stateLink = postedJob.links.find((l: any) => l.rel === 'state')
|
||||
if (!stateLink) {
|
||||
Promise.reject("Job state link was not found.")
|
||||
Promise.reject('Job state link was not found.')
|
||||
}
|
||||
|
||||
const { result: state } = await this.request<string>(
|
||||
@@ -945,31 +945,31 @@ export class SASViyaApiClient {
|
||||
{
|
||||
headers
|
||||
},
|
||||
"text"
|
||||
'text'
|
||||
)
|
||||
|
||||
const currentState = state.trim()
|
||||
if (currentState === "completed") {
|
||||
if (currentState === 'completed') {
|
||||
return Promise.resolve(currentState)
|
||||
}
|
||||
|
||||
return new Promise(async (resolve, _) => {
|
||||
const interval = setInterval(async () => {
|
||||
if (
|
||||
postedJobState === "running" ||
|
||||
postedJobState === "" ||
|
||||
postedJobState === "pending"
|
||||
postedJobState === 'running' ||
|
||||
postedJobState === '' ||
|
||||
postedJobState === 'pending'
|
||||
) {
|
||||
if (stateLink) {
|
||||
if (!silent) {
|
||||
console.log("Polling job status... \n")
|
||||
console.log('Polling job status... \n')
|
||||
}
|
||||
const { result: jobState } = await this.request<string>(
|
||||
`${this.serverUrl}${stateLink.href}?_action=wait&wait=30`,
|
||||
{
|
||||
headers
|
||||
},
|
||||
"text"
|
||||
'text'
|
||||
)
|
||||
|
||||
postedJobState = jobState.trim()
|
||||
@@ -998,25 +998,25 @@ export class SASViyaApiClient {
|
||||
let sessionState = session.state
|
||||
let pollCount = 0
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json",
|
||||
"If-None-Match": etag
|
||||
'Content-Type': 'application/json',
|
||||
'If-None-Match': etag
|
||||
}
|
||||
if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
}
|
||||
const stateLink = session.links.find((l: any) => l.rel === "state")
|
||||
const stateLink = session.links.find((l: any) => l.rel === 'state')
|
||||
return new Promise(async (resolve, _) => {
|
||||
if (sessionState === "pending") {
|
||||
if (sessionState === 'pending') {
|
||||
if (stateLink) {
|
||||
if (!silent) {
|
||||
console.log("Polling session status... \n")
|
||||
console.log('Polling session status... \n')
|
||||
}
|
||||
const { result: state } = await this.request<string>(
|
||||
`${this.serverUrl}${stateLink.href}?wait=30`,
|
||||
{
|
||||
headers
|
||||
},
|
||||
"text"
|
||||
'text'
|
||||
)
|
||||
|
||||
sessionState = state.trim()
|
||||
@@ -1035,7 +1035,7 @@ export class SASViyaApiClient {
|
||||
private async uploadTables(data: any, accessToken?: string) {
|
||||
const uploadedFiles = []
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
@@ -1043,14 +1043,14 @@ export class SASViyaApiClient {
|
||||
|
||||
for (const tableName in data) {
|
||||
const csv = convertToCSV(data[tableName])
|
||||
if (csv === "ERROR: LARGE STRING LENGTH") {
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
throw new Error(
|
||||
"The max length of a string value in SASjs is 32765 characters."
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
)
|
||||
}
|
||||
|
||||
const createFileRequest = {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
body: csv,
|
||||
headers
|
||||
}
|
||||
@@ -1066,9 +1066,9 @@ export class SASViyaApiClient {
|
||||
}
|
||||
|
||||
private async getFolderUri(folderPath: string, accessToken?: string) {
|
||||
const url = "/folders/folders/@item?path=" + folderPath
|
||||
const url = '/folders/folders/@item?path=' + folderPath
|
||||
const requestInfo: any = {
|
||||
method: "GET"
|
||||
method: 'GET'
|
||||
}
|
||||
if (accessToken) {
|
||||
requestInfo.headers = { Authorization: `Bearer ${accessToken}` }
|
||||
@@ -1092,7 +1092,7 @@ export class SASViyaApiClient {
|
||||
private async request<T>(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
contentType: "text" | "json" = "json"
|
||||
contentType: 'text' | 'json' = 'json'
|
||||
) {
|
||||
if (this.csrfToken) {
|
||||
options.headers = {
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
import SASjs from "./index"
|
||||
import SASjs from './index'
|
||||
|
||||
const adapter = new SASjs()
|
||||
|
||||
it("should parse SAS9 source code", async (done) => {
|
||||
it('should parse SAS9 source code', async (done) => {
|
||||
expect(sampleResponse).toBeTruthy()
|
||||
const parsedSourceCode = (adapter as any).parseSAS9SourceCode(sampleResponse)
|
||||
expect(parsedSourceCode).toBeTruthy()
|
||||
const sourceCodeLines = parsedSourceCode.split("\r\n")
|
||||
const sourceCodeLines = parsedSourceCode.split('\r\n')
|
||||
expect(sourceCodeLines.length).toEqual(5)
|
||||
expect(sourceCodeLines[0].startsWith("6")).toBeTruthy()
|
||||
expect(sourceCodeLines[1].startsWith("7")).toBeTruthy()
|
||||
expect(sourceCodeLines[2].startsWith("8")).toBeTruthy()
|
||||
expect(sourceCodeLines[3].startsWith("9")).toBeTruthy()
|
||||
expect(sourceCodeLines[4].startsWith("10")).toBeTruthy()
|
||||
expect(sourceCodeLines[0].startsWith('6')).toBeTruthy()
|
||||
expect(sourceCodeLines[1].startsWith('7')).toBeTruthy()
|
||||
expect(sourceCodeLines[2].startsWith('8')).toBeTruthy()
|
||||
expect(sourceCodeLines[3].startsWith('9')).toBeTruthy()
|
||||
expect(sourceCodeLines[4].startsWith('10')).toBeTruthy()
|
||||
done()
|
||||
})
|
||||
|
||||
it("should parse generated code", async (done) => {
|
||||
it('should parse generated code', async (done) => {
|
||||
expect(sampleResponse).toBeTruthy()
|
||||
const parsedGeneratedCode = (adapter as any).parseGeneratedCode(
|
||||
sampleResponse
|
||||
)
|
||||
expect(parsedGeneratedCode).toBeTruthy()
|
||||
const generatedCodeLines = parsedGeneratedCode.split("\r\n")
|
||||
const generatedCodeLines = parsedGeneratedCode.split('\r\n')
|
||||
expect(generatedCodeLines.length).toEqual(5)
|
||||
expect(generatedCodeLines[0].startsWith("MPRINT(MM_WEBIN)")).toBeTruthy()
|
||||
expect(generatedCodeLines[1].startsWith("MPRINT(MM_WEBLEFT)")).toBeTruthy()
|
||||
expect(generatedCodeLines[2].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy()
|
||||
expect(generatedCodeLines[3].startsWith("MPRINT(MM_WEBRIGHT)")).toBeTruthy()
|
||||
expect(generatedCodeLines[4].startsWith("MPRINT(MM_WEBOUT)")).toBeTruthy()
|
||||
expect(generatedCodeLines[0].startsWith('MPRINT(MM_WEBIN)')).toBeTruthy()
|
||||
expect(generatedCodeLines[1].startsWith('MPRINT(MM_WEBLEFT)')).toBeTruthy()
|
||||
expect(generatedCodeLines[2].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
|
||||
expect(generatedCodeLines[3].startsWith('MPRINT(MM_WEBRIGHT)')).toBeTruthy()
|
||||
expect(generatedCodeLines[4].startsWith('MPRINT(MM_WEBOUT)')).toBeTruthy()
|
||||
done()
|
||||
})
|
||||
|
||||
|
||||
180
src/SASjs.ts
180
src/SASjs.ts
@@ -1,5 +1,5 @@
|
||||
import { isIEorEdgeOrOldFirefox } from "./utils/isIeOrEdge"
|
||||
import * as e6p from "es6-promise"
|
||||
import { isIEorEdgeOrOldFirefox } from './utils/isIeOrEdge'
|
||||
import * as e6p from 'es6-promise'
|
||||
;(e6p as any).polyfill()
|
||||
if (isIEorEdgeOrOldFirefox()) {
|
||||
if (window) {
|
||||
@@ -7,7 +7,7 @@ if (isIEorEdgeOrOldFirefox()) {
|
||||
}
|
||||
}
|
||||
// tslint:disable-next-line
|
||||
require("isomorphic-fetch")
|
||||
require('isomorphic-fetch')
|
||||
import {
|
||||
convertToCSV,
|
||||
compareTimestamps,
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
parseWeboutResponse,
|
||||
needsRetry,
|
||||
asyncForEach
|
||||
} from "./utils"
|
||||
} from './utils'
|
||||
import {
|
||||
SASjsConfig,
|
||||
SASjsRequest,
|
||||
@@ -30,19 +30,19 @@ import {
|
||||
ServerType,
|
||||
CsrfToken,
|
||||
UploadFile
|
||||
} from "./types"
|
||||
import { SASViyaApiClient } from "./SASViyaApiClient"
|
||||
import { SAS9ApiClient } from "./SAS9ApiClient"
|
||||
import { FileUploader } from "./FileUploader"
|
||||
} from './types'
|
||||
import { SASViyaApiClient } from './SASViyaApiClient'
|
||||
import { SAS9ApiClient } from './SAS9ApiClient'
|
||||
import { FileUploader } from './FileUploader'
|
||||
|
||||
const defaultConfig: SASjsConfig = {
|
||||
serverUrl: "",
|
||||
pathSAS9: "/SASStoredProcess/do",
|
||||
pathSASViya: "/SASJobExecution",
|
||||
appLoc: "/Public/seedapp",
|
||||
serverUrl: '',
|
||||
pathSAS9: '/SASStoredProcess/do',
|
||||
pathSASViya: '/SASJobExecution',
|
||||
appLoc: '/Public/seedapp',
|
||||
serverType: ServerType.SASViya,
|
||||
debug: true,
|
||||
contextName: "SAS Job Execution compute context",
|
||||
contextName: 'SAS Job Execution compute context',
|
||||
useComputeApi: false
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ const requestRetryLimit = 5
|
||||
*/
|
||||
export default class SASjs {
|
||||
private sasjsConfig: SASjsConfig = new SASjsConfig()
|
||||
private jobsPath: string = ""
|
||||
private logoutUrl: string = ""
|
||||
private loginUrl: string = ""
|
||||
private jobsPath: string = ''
|
||||
private logoutUrl: string = ''
|
||||
private loginUrl: string = ''
|
||||
private csrfTokenApi: CsrfToken | null = null
|
||||
private csrfTokenWeb: CsrfToken | null = null
|
||||
private retryCountWeb: number = 0
|
||||
@@ -64,7 +64,7 @@ export default class SASjs {
|
||||
private retryCountJeseApi: number = 0
|
||||
private sasjsRequests: SASjsRequest[] = []
|
||||
private sasjsWaitingRequests: SASjsWaitingRequest[] = []
|
||||
private userName: string = ""
|
||||
private userName: string = ''
|
||||
private sasViyaApiClient: SASViyaApiClient | null = null
|
||||
private sas9ApiClient: SAS9ApiClient | null = null
|
||||
private fileUploader: FileUploader | null = null
|
||||
@@ -84,7 +84,7 @@ export default class SASjs {
|
||||
repositoryName: string
|
||||
) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SAS9) {
|
||||
throw new Error("This operation is only supported on SAS9 servers.")
|
||||
throw new Error('This operation is only supported on SAS9 servers.')
|
||||
}
|
||||
return await this.sas9ApiClient?.executeScript(
|
||||
linesOfCode,
|
||||
@@ -95,21 +95,21 @@ export default class SASjs {
|
||||
|
||||
public async getAllContexts(accessToken: string) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.getAllContexts(accessToken)
|
||||
}
|
||||
|
||||
public async getExecutableContexts(accessToken: string) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.getExecutableContexts(accessToken)
|
||||
}
|
||||
|
||||
public async createSession(contextName: string, accessToken: string) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.createSession(contextName, accessToken)
|
||||
}
|
||||
@@ -119,11 +119,11 @@ export default class SASjs {
|
||||
linesOfCode: string[],
|
||||
contextName: string,
|
||||
accessToken?: string,
|
||||
sessionId = "",
|
||||
sessionId = '',
|
||||
silent = false
|
||||
) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.executeScript(
|
||||
fileName,
|
||||
@@ -142,7 +142,7 @@ export default class SASjs {
|
||||
sasApiClient?: SASViyaApiClient
|
||||
) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
if (sasApiClient)
|
||||
return await sasApiClient.createFolder(
|
||||
@@ -168,7 +168,7 @@ export default class SASjs {
|
||||
sasApiClient?: SASViyaApiClient
|
||||
) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
if (sasApiClient)
|
||||
return await sasApiClient!.createJobDefinition(
|
||||
@@ -189,7 +189,7 @@ export default class SASjs {
|
||||
|
||||
public async getAuthCode(clientId: string) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.getAuthCode(clientId)
|
||||
}
|
||||
@@ -200,7 +200,7 @@ export default class SASjs {
|
||||
authCode: string
|
||||
) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.getAccessToken(
|
||||
clientId,
|
||||
@@ -215,7 +215,7 @@ export default class SASjs {
|
||||
refreshToken: string
|
||||
) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.refreshTokens(
|
||||
clientId,
|
||||
@@ -226,7 +226,7 @@ export default class SASjs {
|
||||
|
||||
public async deleteClient(clientId: string, accessToken: string) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
return await this.sasViyaApiClient!.deleteClient(clientId, accessToken)
|
||||
}
|
||||
@@ -289,7 +289,7 @@ export default class SASjs {
|
||||
* @returns a promise which resolves with an object containing two values - a boolean `isLoggedIn`, and a string `userName`
|
||||
*/
|
||||
public async checkSession() {
|
||||
const loginResponse = await fetch(this.loginUrl.replace(".do", ""))
|
||||
const loginResponse = await fetch(this.loginUrl.replace('.do', ''))
|
||||
const responseText = await loginResponse.text()
|
||||
const isLoggedIn = /<button.+onClick.+logout/gm.test(responseText)
|
||||
|
||||
@@ -306,7 +306,7 @@ export default class SASjs {
|
||||
*/
|
||||
public async logIn(username: string, password: string) {
|
||||
const loginParams: any = {
|
||||
_service: "default",
|
||||
_service: 'default',
|
||||
username,
|
||||
password
|
||||
}
|
||||
@@ -331,12 +331,12 @@ export default class SASjs {
|
||||
const loginParamsStr = serialize(loginParams)
|
||||
|
||||
return fetch(this.loginUrl, {
|
||||
method: "post",
|
||||
credentials: "include",
|
||||
referrerPolicy: "same-origin",
|
||||
method: 'post',
|
||||
credentials: 'include',
|
||||
referrerPolicy: 'same-origin',
|
||||
body: loginParamsStr,
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
})
|
||||
})
|
||||
.then((response) => response.text())
|
||||
@@ -438,7 +438,7 @@ export default class SASjs {
|
||||
...config
|
||||
}
|
||||
|
||||
sasJob = sasJob.startsWith("/") ? sasJob.replace("/", "") : sasJob
|
||||
sasJob = sasJob.startsWith('/') ? sasJob.replace('/', '') : sasJob
|
||||
|
||||
if (config.serverType === ServerType.SASViya && config.contextName) {
|
||||
if (config.useComputeApi) {
|
||||
@@ -492,7 +492,7 @@ export default class SASjs {
|
||||
accessToken?: string
|
||||
) {
|
||||
if (this.sasjsConfig.serverType !== ServerType.SASViya) {
|
||||
throw new Error("This operation is only supported on SAS Viya servers.")
|
||||
throw new Error('This operation is only supported on SAS Viya servers.')
|
||||
}
|
||||
|
||||
let sasApiClient: any = null
|
||||
@@ -527,12 +527,12 @@ export default class SASjs {
|
||||
// members of type 'folder' should be processed first
|
||||
if (serviceJson.members[0].members) {
|
||||
serviceJson.members[0].members.sort((member: { type: string }) =>
|
||||
member.type === "folder" ? -1 : 1
|
||||
member.type === 'folder' ? -1 : 1
|
||||
)
|
||||
}
|
||||
|
||||
const members =
|
||||
serviceJson.members[0].name === "services"
|
||||
serviceJson.members[0].name === 'services'
|
||||
? serviceJson.members[0].members
|
||||
: serviceJson.members
|
||||
|
||||
@@ -606,7 +606,7 @@ export default class SASjs {
|
||||
resolve(retryResponse)
|
||||
} else {
|
||||
this.retryCountComputeApi = 0
|
||||
reject({ MESSAGE: "Compute API retry requests limit reached" })
|
||||
reject({ MESSAGE: 'Compute API retry requests limit reached' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,7 +617,7 @@ export default class SASjs {
|
||||
sasjsWaitingRequest.config = config
|
||||
this.sasjsWaitingRequests.push(sasjsWaitingRequest)
|
||||
} else {
|
||||
reject({ MESSAGE: error || "Job execution failed" })
|
||||
reject({ MESSAGE: error || 'Job execution failed' })
|
||||
}
|
||||
|
||||
this.appendSasjsRequest(response.log, sasJob, null)
|
||||
@@ -699,11 +699,11 @@ export default class SASjs {
|
||||
resolve(retryResponse)
|
||||
} else {
|
||||
this.retryCountJeseApi = 0
|
||||
reject({ MESSAGE: "Jes API retry requests limit reached" })
|
||||
reject({ MESSAGE: 'Jes API retry requests limit reached' })
|
||||
}
|
||||
}
|
||||
|
||||
reject({ MESSAGE: (e && e.message) || "Job execution failed" })
|
||||
reject({ MESSAGE: (e && e.message) || 'Job execution failed' })
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -728,14 +728,14 @@ export default class SASjs {
|
||||
data
|
||||
}
|
||||
const program = config.appLoc
|
||||
? config.appLoc.replace(/\/?$/, "/") + sasJob.replace(/^\//, "")
|
||||
? config.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
|
||||
: sasJob
|
||||
const jobUri =
|
||||
config.serverType === "SASVIYA" ? await this.getJobUri(sasJob) : ""
|
||||
config.serverType === 'SASVIYA' ? await this.getJobUri(sasJob) : ''
|
||||
const apiUrl = `${config.serverUrl}${this.jobsPath}/?${
|
||||
jobUri.length > 0
|
||||
? "__program=" + program + "&_job=" + jobUri
|
||||
: "_program=" + program
|
||||
? '__program=' + program + '&_job=' + jobUri
|
||||
: '_program=' + program
|
||||
}`
|
||||
|
||||
const requestParams = {
|
||||
@@ -745,14 +745,14 @@ export default class SASjs {
|
||||
const formData = new FormData()
|
||||
|
||||
let isError = false
|
||||
let errorMsg = ""
|
||||
let errorMsg = ''
|
||||
|
||||
if (data) {
|
||||
const stringifiedData = JSON.stringify(data)
|
||||
if (
|
||||
config.serverType === ServerType.SAS9 ||
|
||||
stringifiedData.length > 500000 ||
|
||||
stringifiedData.includes(";")
|
||||
stringifiedData.includes(';')
|
||||
) {
|
||||
// file upload approach
|
||||
for (const tableName in data) {
|
||||
@@ -761,14 +761,14 @@ export default class SASjs {
|
||||
}
|
||||
const name = tableName
|
||||
const csv = convertToCSV(data[tableName])
|
||||
if (csv === "ERROR: LARGE STRING LENGTH") {
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
isError = true
|
||||
errorMsg =
|
||||
"The max length of a string value in SASjs is 32765 characters."
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
}
|
||||
|
||||
const file = new Blob([csv], {
|
||||
type: "application/csv"
|
||||
type: 'application/csv'
|
||||
})
|
||||
|
||||
formData.append(name, file, `${name}.csv`)
|
||||
@@ -784,10 +784,10 @@ export default class SASjs {
|
||||
tableCounter++
|
||||
sasjsTables.push(tableName)
|
||||
const csv = convertToCSV(data[tableName])
|
||||
if (csv === "ERROR: LARGE STRING LENGTH") {
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
isError = true
|
||||
errorMsg =
|
||||
"The max length of a string value in SASjs is 32765 characters."
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
}
|
||||
// if csv has length more then 16k, send in chunks
|
||||
if (csv.length > 16000) {
|
||||
@@ -800,7 +800,7 @@ export default class SASjs {
|
||||
requestParams[`sasjs${tableCounter}data`] = csv
|
||||
}
|
||||
}
|
||||
requestParams["sasjs_tables"] = sasjsTables.join(" ")
|
||||
requestParams['sasjs_tables'] = sasjsTables.join(' ')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,21 +822,21 @@ export default class SASjs {
|
||||
headers[this.csrfTokenWeb.headerName] = this.csrfTokenWeb.value
|
||||
}
|
||||
fetch(apiUrl, {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
referrerPolicy: "same-origin",
|
||||
referrerPolicy: 'same-origin',
|
||||
headers
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 403) {
|
||||
const tokenHeader = response.headers.get("X-CSRF-HEADER")
|
||||
const tokenHeader = response.headers.get('X-CSRF-HEADER')
|
||||
|
||||
if (tokenHeader) {
|
||||
const token = response.headers.get(tokenHeader)
|
||||
this.csrfTokenWeb = {
|
||||
headerName: tokenHeader,
|
||||
value: token || ""
|
||||
value: token || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -878,7 +878,7 @@ export default class SASjs {
|
||||
this.updateUsername(responseText)
|
||||
const jsonResponseText = parseWeboutResponse(responseText)
|
||||
|
||||
if (jsonResponseText !== "") {
|
||||
if (jsonResponseText !== '') {
|
||||
resolve(JSON.parse(jsonResponseText))
|
||||
} else {
|
||||
reject({
|
||||
@@ -954,14 +954,14 @@ export default class SASjs {
|
||||
const requestParams: any = {}
|
||||
|
||||
if (this.csrfTokenWeb) {
|
||||
requestParams["_csrf"] = this.csrfTokenWeb.value
|
||||
requestParams['_csrf'] = this.csrfTokenWeb.value
|
||||
}
|
||||
|
||||
if (config.debug) {
|
||||
requestParams["_omittextlog"] = "false"
|
||||
requestParams["_omitsessionresults"] = "false"
|
||||
requestParams['_omittextlog'] = 'false'
|
||||
requestParams['_omitsessionresults'] = 'false'
|
||||
|
||||
requestParams["_debug"] = 131
|
||||
requestParams['_debug'] = 131
|
||||
}
|
||||
|
||||
return requestParams
|
||||
@@ -971,12 +971,12 @@ export default class SASjs {
|
||||
try {
|
||||
const responseJson = JSON.parse(response)
|
||||
if (this.sasjsConfig.serverType === ServerType.SAS9) {
|
||||
this.userName = responseJson["_METAUSER"]
|
||||
this.userName = responseJson['_METAUSER']
|
||||
} else {
|
||||
this.userName = responseJson["SYSUSERID"]
|
||||
this.userName = responseJson['SYSUSERID']
|
||||
}
|
||||
} catch (e) {
|
||||
this.userName = ""
|
||||
this.userName = ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,24 +994,24 @@ export default class SASjs {
|
||||
resolve(resText)
|
||||
})
|
||||
} else {
|
||||
reject("No debug info in response")
|
||||
reject('No debug info in response')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async getJobUri(sasJob: string) {
|
||||
if (!this.sasViyaApiClient) return ""
|
||||
if (!this.sasViyaApiClient) return ''
|
||||
const jobMap: any = await this.sasViyaApiClient.getAppLocMap()
|
||||
let uri = ""
|
||||
let uri = ''
|
||||
|
||||
if (jobMap.size) {
|
||||
const jobKey = sasJob.split("/")[0]
|
||||
const jobName = sasJob.split("/")[1]
|
||||
const jobKey = sasJob.split('/')[0]
|
||||
const jobName = sasJob.split('/')[1]
|
||||
|
||||
const locJobs = jobMap.get(jobKey)
|
||||
if (locJobs) {
|
||||
const job = locJobs.find(
|
||||
(el: any) => el.name === jobName && el.contentType === "jobDefinition"
|
||||
(el: any) => el.name === jobName && el.contentType === 'jobDefinition'
|
||||
)
|
||||
if (job) {
|
||||
uri = job.uri
|
||||
@@ -1022,14 +1022,14 @@ export default class SASjs {
|
||||
}
|
||||
|
||||
private parseSAS9ErrorResponse(response: string) {
|
||||
const logLines = response.split("\n")
|
||||
const logLines = response.split('\n')
|
||||
const parsedLines: string[] = []
|
||||
let firstErrorLineIndex: number = -1
|
||||
|
||||
logLines.map((line: string, index: number) => {
|
||||
if (
|
||||
line.toLowerCase().includes("error") &&
|
||||
!line.toLowerCase().includes("this request completed with errors.") &&
|
||||
line.toLowerCase().includes('error') &&
|
||||
!line.toLowerCase().includes('this request completed with errors.') &&
|
||||
firstErrorLineIndex === -1
|
||||
) {
|
||||
firstErrorLineIndex = index
|
||||
@@ -1040,7 +1040,7 @@ export default class SASjs {
|
||||
parsedLines.push(logLines[i])
|
||||
}
|
||||
|
||||
return parsedLines.join(", ")
|
||||
return parsedLines.join(', ')
|
||||
}
|
||||
|
||||
private parseLogFromResponse(response: any, program: string) {
|
||||
@@ -1058,7 +1058,7 @@ export default class SASjs {
|
||||
private fetchLogFileContent(logLink: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(logLink, {
|
||||
method: "GET"
|
||||
method: 'GET'
|
||||
})
|
||||
.then((response: any) => response.text())
|
||||
.then((response: any) => resolve(response))
|
||||
@@ -1071,8 +1071,8 @@ export default class SASjs {
|
||||
program: string,
|
||||
pgmData: any
|
||||
) {
|
||||
let sourceCode = ""
|
||||
let generatedCode = ""
|
||||
let sourceCode = ''
|
||||
let generatedCode = ''
|
||||
let sasWork = null
|
||||
|
||||
if (response && response.result && response.log) {
|
||||
@@ -1154,7 +1154,7 @@ export default class SASjs {
|
||||
private setupConfiguration() {
|
||||
if (
|
||||
this.sasjsConfig.serverUrl === undefined ||
|
||||
this.sasjsConfig.serverUrl === ""
|
||||
this.sasjsConfig.serverUrl === ''
|
||||
) {
|
||||
let url = `${location.protocol}//${location.hostname}`
|
||||
if (location.port) {
|
||||
@@ -1163,7 +1163,7 @@ export default class SASjs {
|
||||
this.sasjsConfig.serverUrl = url
|
||||
}
|
||||
|
||||
if (this.sasjsConfig.serverUrl.slice(-1) === "/") {
|
||||
if (this.sasjsConfig.serverUrl.slice(-1) === '/') {
|
||||
this.sasjsConfig.serverUrl = this.sasjsConfig.serverUrl.slice(0, -1)
|
||||
}
|
||||
|
||||
@@ -1174,8 +1174,8 @@ export default class SASjs {
|
||||
this.loginUrl = `${this.sasjsConfig.serverUrl}/SASLogon/login`
|
||||
this.logoutUrl =
|
||||
this.sasjsConfig.serverType === ServerType.SAS9
|
||||
? "/SASLogon/logout?"
|
||||
: "/SASLogon/logout.do?"
|
||||
? '/SASLogon/logout?'
|
||||
: '/SASLogon/logout.do?'
|
||||
|
||||
if (this.sasjsConfig.serverType === ServerType.SASViya) {
|
||||
if (this.sasViyaApiClient)
|
||||
@@ -1206,8 +1206,8 @@ export default class SASjs {
|
||||
}
|
||||
|
||||
private setLoginUrl = (matches: RegExpExecArray) => {
|
||||
let parsedURL = matches[1].replace(/\?.*/, "")
|
||||
if (parsedURL[0] === "/") {
|
||||
let parsedURL = matches[1].replace(/\?.*/, '')
|
||||
if (parsedURL[0] === '/') {
|
||||
parsedURL = parsedURL.substr(1)
|
||||
|
||||
const tempLoginLink = this.sasjsConfig.serverUrl
|
||||
@@ -1219,7 +1219,7 @@ export default class SASjs {
|
||||
this.loginUrl =
|
||||
this.sasjsConfig.serverType === ServerType.SASViya
|
||||
? tempLoginLink
|
||||
: loginUrl.replace(".do", "")
|
||||
: loginUrl.replace('.do', '')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1251,7 +1251,7 @@ export default class SASjs {
|
||||
) {
|
||||
await asyncForEach(membersJson, async (member: any) => {
|
||||
switch (member.type) {
|
||||
case "folder":
|
||||
case 'folder':
|
||||
await this.createFolder(
|
||||
member.name,
|
||||
parentFolder,
|
||||
@@ -1260,7 +1260,7 @@ export default class SASjs {
|
||||
sasApiClient
|
||||
)
|
||||
break
|
||||
case "service":
|
||||
case 'service':
|
||||
await this.createJobDefinition(
|
||||
member.name,
|
||||
member.code,
|
||||
@@ -1273,7 +1273,7 @@ export default class SASjs {
|
||||
default:
|
||||
throw new Error(`Unidenitied member present in Json: ${member.name}`)
|
||||
}
|
||||
if (member.type === "folder" && member.members && member.members.length)
|
||||
if (member.type === 'folder' && member.members && member.members.length)
|
||||
await this.createFoldersAndServices(
|
||||
`${parentFolder}/${member.name}`,
|
||||
member.members,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Session, Context, CsrfToken } from "./types"
|
||||
import { asyncForEach, makeRequest } from "./utils"
|
||||
import { Session, Context, CsrfToken } from './types'
|
||||
import { asyncForEach, makeRequest } from './utils'
|
||||
|
||||
const MAX_SESSION_COUNT = 1
|
||||
|
||||
@@ -32,7 +32,7 @@ export class SessionManager {
|
||||
|
||||
async clearSession(id: string, accessToken?: string) {
|
||||
const deleteSessionRequest = {
|
||||
method: "DELETE",
|
||||
method: 'DELETE',
|
||||
headers: this.getHeaders(accessToken)
|
||||
}
|
||||
return await this.request<Session>(
|
||||
@@ -57,7 +57,7 @@ export class SessionManager {
|
||||
|
||||
private async createAndWaitForSession(accessToken?: string) {
|
||||
const createSessionRequest = {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(accessToken)
|
||||
}
|
||||
const { result: createdSession, etag } = await this.request<Session>(
|
||||
@@ -99,7 +99,7 @@ export class SessionManager {
|
||||
|
||||
private getHeaders(accessToken?: string) {
|
||||
const headers: any = {
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`
|
||||
@@ -117,21 +117,21 @@ export class SessionManager {
|
||||
let sessionState = session.state
|
||||
const headers: any = {
|
||||
...this.getHeaders(accessToken),
|
||||
"If-None-Match": etag
|
||||
'If-None-Match': etag
|
||||
}
|
||||
const stateLink = session.links.find((l: any) => l.rel === "state")
|
||||
const stateLink = session.links.find((l: any) => l.rel === 'state')
|
||||
return new Promise(async (resolve, _) => {
|
||||
if (sessionState === "pending") {
|
||||
if (sessionState === 'pending') {
|
||||
if (stateLink) {
|
||||
if (!silent) {
|
||||
console.log("Polling session status... \n")
|
||||
console.log('Polling session status... \n')
|
||||
}
|
||||
const { result: state } = await this.request<string>(
|
||||
`${this.serverUrl}${stateLink.href}?wait=30`,
|
||||
{
|
||||
headers
|
||||
},
|
||||
"text"
|
||||
'text'
|
||||
)
|
||||
|
||||
sessionState = state.trim()
|
||||
@@ -149,7 +149,7 @@ export class SessionManager {
|
||||
private async request<T>(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
contentType: "text" | "json" = "json"
|
||||
contentType: 'text' | 'json' = 'json'
|
||||
) {
|
||||
if (this.csrfToken) {
|
||||
options.headers = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SASjs from "./SASjs"
|
||||
export * from "./types"
|
||||
export * from "./SASViyaApiClient"
|
||||
export * from "./SAS9ApiClient"
|
||||
import SASjs from './SASjs'
|
||||
export * from './types'
|
||||
export * from './SASViyaApiClient'
|
||||
export * from './SAS9ApiClient'
|
||||
export default SASjs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from "./Link"
|
||||
import { Link } from './Link'
|
||||
|
||||
export interface Folder {
|
||||
id: string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Link } from "./Link"
|
||||
import { JobResult } from "./JobResult"
|
||||
import { Link } from './Link'
|
||||
import { JobResult } from './JobResult'
|
||||
|
||||
export interface Job {
|
||||
id: string
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export interface JobResult {
|
||||
"_webout.json": string
|
||||
'_webout.json': string
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ServerType } from "./ServerType"
|
||||
import { ServerType } from './ServerType'
|
||||
|
||||
/**
|
||||
* Specifies the configuration for the SASjs instance.
|
||||
@@ -10,14 +10,14 @@ export class SASjsConfig {
|
||||
* Can be omitted, eg if serving directly from the SAS Web Server or being
|
||||
* streamed.
|
||||
*/
|
||||
serverUrl: string = ""
|
||||
pathSAS9: string = ""
|
||||
pathSASViya: string = ""
|
||||
serverUrl: string = ''
|
||||
pathSAS9: string = ''
|
||||
pathSASViya: string = ''
|
||||
/**
|
||||
* The appLoc is the parent folder under which the SAS services (STPs or Job
|
||||
* Execution Services) are stored.
|
||||
*/
|
||||
appLoc: string = ""
|
||||
appLoc: string = ''
|
||||
/**
|
||||
* Can be SAS9 or SASVIYA
|
||||
*/
|
||||
@@ -26,6 +26,6 @@ export class SASjsConfig {
|
||||
* Set to `true` to enable additional debugging.
|
||||
*/
|
||||
debug: boolean = true
|
||||
contextName: string = ""
|
||||
contextName: string = ''
|
||||
useComputeApi = false
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*
|
||||
*/
|
||||
export enum ServerType {
|
||||
SASViya = "SASVIYA",
|
||||
SAS9 = "SAS9"
|
||||
SASViya = 'SASVIYA',
|
||||
SAS9 = 'SAS9'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from "./Link"
|
||||
import { Link } from './Link'
|
||||
|
||||
export interface Session {
|
||||
id: string
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export * from "./Context"
|
||||
export * from "./CsrfToken"
|
||||
export * from "./Folder"
|
||||
export * from "./Job"
|
||||
export * from "./Link"
|
||||
export * from "./SASjsConfig"
|
||||
export * from "./SASjsRequest"
|
||||
export * from "./SASjsWaitingRequest"
|
||||
export * from "./ServerType"
|
||||
export * from "./Session"
|
||||
export * from "./UploadFile"
|
||||
export * from './Context'
|
||||
export * from './CsrfToken'
|
||||
export * from './Folder'
|
||||
export * from './Job'
|
||||
export * from './Link'
|
||||
export * from './SASjsConfig'
|
||||
export * from './SASjsRequest'
|
||||
export * from './SASjsWaitingRequest'
|
||||
export * from './ServerType'
|
||||
export * from './Session'
|
||||
export * from './UploadFile'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SASjsRequest } from "../types/SASjsRequest"
|
||||
import { SASjsRequest } from '../types/SASjsRequest'
|
||||
|
||||
/**
|
||||
* Comparator for SASjs request timestamps
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @param data - the JSON object to convert.
|
||||
*/
|
||||
export const convertToCSV = (data: any) => {
|
||||
const replacer = (key: any, value: any) => (value === null ? "" : value)
|
||||
const replacer = (key: any, value: any) => (value === null ? '' : value)
|
||||
const headerFields = Object.keys(data[0])
|
||||
let csvTest
|
||||
let invalidString = false
|
||||
@@ -14,31 +14,31 @@ export const convertToCSV = (data: any) => {
|
||||
|
||||
const longestValueForField = data
|
||||
.map((row: any, index: number) => {
|
||||
if (row[field] || row[field] === "") {
|
||||
if (row[field] || row[field] === '') {
|
||||
if (firstFoundType) {
|
||||
let currentFieldType =
|
||||
row[field] === "" || typeof row[field] === "string"
|
||||
? "chars"
|
||||
: "number"
|
||||
row[field] === '' || typeof row[field] === 'string'
|
||||
? 'chars'
|
||||
: 'number'
|
||||
|
||||
if (!hasMixedTypes) {
|
||||
hasMixedTypes = currentFieldType !== firstFoundType
|
||||
rowNumError = hasMixedTypes ? index + 1 : -1
|
||||
}
|
||||
} else {
|
||||
if (row[field] === "") {
|
||||
firstFoundType = "chars"
|
||||
if (row[field] === '') {
|
||||
firstFoundType = 'chars'
|
||||
} else {
|
||||
firstFoundType =
|
||||
typeof row[field] === "string" ? "chars" : "number"
|
||||
typeof row[field] === 'string' ? 'chars' : 'number'
|
||||
}
|
||||
}
|
||||
|
||||
let byteSize
|
||||
|
||||
if (typeof row[field] === "string") {
|
||||
if (typeof row[field] === 'string') {
|
||||
let doubleQuotesFound = row[field]
|
||||
.split("")
|
||||
.split('')
|
||||
.filter((char: any) => char === '"')
|
||||
|
||||
byteSize = getByteSize(row[field])
|
||||
@@ -61,17 +61,17 @@ export const convertToCSV = (data: any) => {
|
||||
)
|
||||
}
|
||||
|
||||
return `${field}:${firstFoundType === "chars" ? "$" : ""}${
|
||||
return `${field}:${firstFoundType === 'chars' ? '$' : ''}${
|
||||
longestValueForField
|
||||
? longestValueForField
|
||||
: firstFoundType === "chars"
|
||||
? "1"
|
||||
: "best"
|
||||
: firstFoundType === 'chars'
|
||||
? '1'
|
||||
: 'best'
|
||||
}.`
|
||||
})
|
||||
|
||||
if (invalidString) {
|
||||
return "ERROR: LARGE STRING LENGTH"
|
||||
return 'ERROR: LARGE STRING LENGTH'
|
||||
}
|
||||
csvTest = data.map((row: any) => {
|
||||
const fields = Object.keys(row).map((fieldName, index) => {
|
||||
@@ -86,15 +86,15 @@ export const convertToCSV = (data: any) => {
|
||||
value = JSON.stringify(currentCell, replacer)
|
||||
}
|
||||
|
||||
value = value.replace(/\\\\/gm, "\\")
|
||||
value = value.replace(/\\\\/gm, '\\')
|
||||
|
||||
if (containsSpecialChar) {
|
||||
if (value.includes(",") || value.includes('"')) {
|
||||
if (value.includes(',') || value.includes('"')) {
|
||||
value = '"' + value + '"'
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!value.includes(",") &&
|
||||
!value.includes(',') &&
|
||||
value.includes('"') &&
|
||||
!value.includes('\\"')
|
||||
) {
|
||||
@@ -104,19 +104,19 @@ export const convertToCSV = (data: any) => {
|
||||
value = value.replace(/\\"/gm, '""')
|
||||
}
|
||||
|
||||
value = value.replace(/\r\n/gm, "\n")
|
||||
value = value.replace(/\r\n/gm, '\n')
|
||||
|
||||
if (value === "" && headers[index].includes("best")) {
|
||||
value = "."
|
||||
if (value === '' && headers[index].includes('best')) {
|
||||
value = '.'
|
||||
}
|
||||
|
||||
return value
|
||||
})
|
||||
return fields.join(",")
|
||||
return fields.join(',')
|
||||
})
|
||||
|
||||
let finalCSV =
|
||||
headers.join(",").replace(/,/g, " ") + "\r\n" + csvTest.join("\r\n")
|
||||
headers.join(',').replace(/,/g, ' ') + '\r\n' + csvTest.join('\r\n')
|
||||
|
||||
return finalCSV
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { convertToCSV } from "./convertToCsv"
|
||||
import { splitChunks } from "./splitChunks"
|
||||
import { convertToCSV } from './convertToCsv'
|
||||
import { splitChunks } from './splitChunks'
|
||||
|
||||
export const formatDataForRequest = (data: any) => {
|
||||
const sasjsTables = []
|
||||
@@ -10,9 +10,9 @@ export const formatDataForRequest = (data: any) => {
|
||||
tableCounter++
|
||||
sasjsTables.push(tableName)
|
||||
const csv = convertToCSV(data[tableName])
|
||||
if (csv === "ERROR: LARGE STRING LENGTH") {
|
||||
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
||||
throw new Error(
|
||||
"The max length of a string value in SASjs is 32765 characters."
|
||||
'The max length of a string value in SASjs is 32765 characters.'
|
||||
)
|
||||
}
|
||||
// if csv has length more then 16k, send in chunks
|
||||
@@ -27,7 +27,7 @@ export const formatDataForRequest = (data: any) => {
|
||||
result[`sasjs${tableCounter}data`] = csv
|
||||
}
|
||||
}
|
||||
result["sasjs_tables"] = sasjsTables.join(" ")
|
||||
result['sasjs_tables'] = sasjsTables.join(' ')
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
export * from "./asyncForEach"
|
||||
export * from "./compareTimestamps"
|
||||
export * from "./convertToCsv"
|
||||
export * from "./isAuthorizeFormRequired"
|
||||
export * from "./isLoginRequired"
|
||||
export * from "./isLoginSuccess"
|
||||
export * from "./makeRequest"
|
||||
export * from "./needsRetry"
|
||||
export * from "./parseAndSubmitAuthorizeForm"
|
||||
export * from "./parseGeneratedCode"
|
||||
export * from "./parseSourceCode"
|
||||
export * from "./parseSasViyaLog"
|
||||
export * from "./serialize"
|
||||
export * from "./splitChunks"
|
||||
export * from "./parseWeboutResponse"
|
||||
export * from './asyncForEach'
|
||||
export * from './compareTimestamps'
|
||||
export * from './convertToCsv'
|
||||
export * from './isAuthorizeFormRequired'
|
||||
export * from './isLoginRequired'
|
||||
export * from './isLoginSuccess'
|
||||
export * from './makeRequest'
|
||||
export * from './needsRetry'
|
||||
export * from './parseAndSubmitAuthorizeForm'
|
||||
export * from './parseGeneratedCode'
|
||||
export * from './parseSourceCode'
|
||||
export * from './parseSasViyaLog'
|
||||
export * from './serialize'
|
||||
export * from './splitChunks'
|
||||
export * from './parseWeboutResponse'
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
export function isIEorEdgeOrOldFirefox() {
|
||||
if (typeof window === "undefined") {
|
||||
if (typeof window === 'undefined') {
|
||||
return false
|
||||
}
|
||||
const ua = window.navigator.userAgent
|
||||
|
||||
if (ua.indexOf("Firefox") > 0) {
|
||||
if (ua.indexOf('Firefox') > 0) {
|
||||
const version = parseInt(
|
||||
ua.substring(ua.lastIndexOf("Firefox/") + 8, ua.length),
|
||||
ua.substring(ua.lastIndexOf('Firefox/') + 8, ua.length),
|
||||
10
|
||||
)
|
||||
return version <= 60
|
||||
}
|
||||
|
||||
const msie = ua.indexOf("MSIE ")
|
||||
const msie = ua.indexOf('MSIE ')
|
||||
if (msie > 0) {
|
||||
// IE 10 or older => return version number
|
||||
return true
|
||||
}
|
||||
|
||||
const trident = ua.indexOf("Trident/")
|
||||
const trident = ua.indexOf('Trident/')
|
||||
if (trident > 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
const edge = ua.indexOf("Edge/")
|
||||
const edge = ua.indexOf('Edge/')
|
||||
if (edge > 0) {
|
||||
// Edge (IE 12+) => return version number
|
||||
return true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CsrfToken } from "../types"
|
||||
import { needsRetry } from "./needsRetry"
|
||||
import { CsrfToken } from '../types'
|
||||
import { needsRetry } from './needsRetry'
|
||||
|
||||
let retryCount: number = 0
|
||||
let retryLimit: number = 5
|
||||
@@ -8,28 +8,28 @@ export async function makeRequest<T>(
|
||||
url: string,
|
||||
request: RequestInit,
|
||||
callback: (value: CsrfToken) => any,
|
||||
contentType: "text" | "json" = "json"
|
||||
contentType: 'text' | 'json' = 'json'
|
||||
): Promise<{ result: T; etag: string | null }> {
|
||||
let retryRequest: any = null
|
||||
|
||||
const responseTransform =
|
||||
contentType === "json"
|
||||
contentType === 'json'
|
||||
? (res: Response) => res.json()
|
||||
: (res: Response) => res.text()
|
||||
let etag = null
|
||||
const result = await fetch(url, request).then(async (response) => {
|
||||
if (response.redirected && response.url.includes("SASLogon/login")) {
|
||||
if (response.redirected && response.url.includes('SASLogon/login')) {
|
||||
return Promise.reject({ status: 401 })
|
||||
}
|
||||
if (!response.ok) {
|
||||
if (response.status === 403) {
|
||||
const tokenHeader = response.headers.get("X-CSRF-HEADER")
|
||||
const tokenHeader = response.headers.get('X-CSRF-HEADER')
|
||||
|
||||
if (tokenHeader) {
|
||||
const token = response.headers.get(tokenHeader)
|
||||
callback({
|
||||
headerName: tokenHeader,
|
||||
value: token || ""
|
||||
value: token || ''
|
||||
})
|
||||
|
||||
retryRequest = {
|
||||
@@ -37,7 +37,7 @@ export async function makeRequest<T>(
|
||||
headers: { ...request.headers, [tokenHeader]: token }
|
||||
}
|
||||
return fetch(url, retryRequest).then((res) => {
|
||||
etag = res.headers.get("ETag")
|
||||
etag = res.headers.get('ETag')
|
||||
return responseTransform(res)
|
||||
})
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export async function makeRequest<T>(
|
||||
} else {
|
||||
retryCount = 0
|
||||
|
||||
throw new Error("Request retry limit exceeded")
|
||||
throw new Error('Request retry limit exceeded')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ export async function makeRequest<T>(
|
||||
return Promise.resolve()
|
||||
}
|
||||
const responseTransformed = await responseTransform(response)
|
||||
let responseText = ""
|
||||
let responseText = ''
|
||||
|
||||
if (typeof responseTransformed === "string") {
|
||||
if (typeof responseTransformed === 'string') {
|
||||
responseText = responseTransformed
|
||||
} else {
|
||||
responseText = JSON.stringify(responseTransformed)
|
||||
@@ -95,11 +95,11 @@ export async function makeRequest<T>(
|
||||
} else {
|
||||
retryCount = 0
|
||||
|
||||
throw new Error("Request retry limit exceeded")
|
||||
throw new Error('Request retry limit exceeded')
|
||||
}
|
||||
}
|
||||
|
||||
etag = response.headers.get("ETag")
|
||||
etag = response.headers.get('ETag')
|
||||
return responseTransformed
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,13 +2,13 @@ export const needsRetry = (responseText: string): boolean => {
|
||||
return (
|
||||
!!responseText &&
|
||||
((responseText.includes('"errorCode":403') &&
|
||||
responseText.includes("_csrf") &&
|
||||
responseText.includes("X-CSRF-TOKEN")) ||
|
||||
responseText.includes('_csrf') &&
|
||||
responseText.includes('X-CSRF-TOKEN')) ||
|
||||
(responseText.includes('"status":403') &&
|
||||
responseText.includes('"error":"Forbidden"')) ||
|
||||
(responseText.includes('"status":449') &&
|
||||
responseText.includes(
|
||||
"Authentication success, retry original request"
|
||||
'Authentication success, retry original request'
|
||||
)))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ export const parseAndSubmitAuthorizeForm = async (
|
||||
let authUrl: string | null = null
|
||||
const params: any = {}
|
||||
|
||||
const responseBody = response.split("<body>")[1].split("</body>")[0]
|
||||
const bodyElement = document.createElement("div")
|
||||
const responseBody = response.split('<body>')[1].split('</body>')[0]
|
||||
const bodyElement = document.createElement('div')
|
||||
bodyElement.innerHTML = responseBody
|
||||
|
||||
const form = bodyElement.querySelector("#application_authorization")
|
||||
authUrl = form ? serverUrl + form.getAttribute("action") : null
|
||||
const form = bodyElement.querySelector('#application_authorization')
|
||||
authUrl = form ? serverUrl + form.getAttribute('action') : null
|
||||
|
||||
const inputs: any = form?.querySelectorAll("input")
|
||||
const inputs: any = form?.querySelectorAll('input')
|
||||
|
||||
for (const input of inputs) {
|
||||
if (input.name === "user_oauth_approval") {
|
||||
input.value = "true"
|
||||
if (input.name === 'user_oauth_approval') {
|
||||
input.value = 'true'
|
||||
}
|
||||
|
||||
params[input.name] = input.value
|
||||
@@ -33,17 +33,17 @@ export const parseAndSubmitAuthorizeForm = async (
|
||||
return new Promise((resolve, reject) => {
|
||||
if (authUrl) {
|
||||
fetch(authUrl, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: formData,
|
||||
referrerPolicy: "same-origin"
|
||||
referrerPolicy: 'same-origin'
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((res) => {
|
||||
resolve(res)
|
||||
})
|
||||
} else {
|
||||
reject("Auth form url is null")
|
||||
reject('Auth form url is null')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export const parseGeneratedCode = (log: string) => {
|
||||
const startsWith = "MPRINT"
|
||||
const startsWith = 'MPRINT'
|
||||
const isGeneratedCodeLine = (line: string) =>
|
||||
line.trim().startsWith(startsWith)
|
||||
const logLines = log.split("\n").filter(isGeneratedCodeLine)
|
||||
return logLines.join("\r\n")
|
||||
const logLines = log.split('\n').filter(isGeneratedCodeLine)
|
||||
return logLines.join('\r\n')
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ export const parseSasViyaLog = (logResponse: { items: any[] }) => {
|
||||
let log
|
||||
try {
|
||||
log = logResponse.items
|
||||
? logResponse.items.map((i) => i.line).join("\n")
|
||||
? logResponse.items.map((i) => i.line).join('\n')
|
||||
: JSON.stringify(logResponse)
|
||||
} catch (e) {
|
||||
console.error("An error has occurred while parsing the log response", e)
|
||||
console.error('An error has occurred while parsing the log response', e)
|
||||
log = logResponse
|
||||
}
|
||||
return log
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const parseSourceCode = (log: string): string => {
|
||||
const isSourceCodeLine = (line: string) =>
|
||||
line.trim().substring(0, 10).trimStart().match(/^\d/)
|
||||
const logLines = log.split("\n").filter(isSourceCodeLine)
|
||||
return logLines.join("\r\n")
|
||||
const logLines = log.split('\n').filter(isSourceCodeLine)
|
||||
return logLines.join('\r\n')
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
export const parseWeboutResponse = (response: string) => {
|
||||
let sasResponse = ""
|
||||
let sasResponse = ''
|
||||
|
||||
if (response.includes(">>weboutBEGIN<<")) {
|
||||
if (response.includes('>>weboutBEGIN<<')) {
|
||||
try {
|
||||
sasResponse = response
|
||||
.split(">>weboutBEGIN<<")[1]
|
||||
.split(">>weboutEND<<")[0]
|
||||
.split('>>weboutBEGIN<<')[1]
|
||||
.split('>>weboutEND<<')[0]
|
||||
} catch (e) {
|
||||
sasResponse = ""
|
||||
sasResponse = ''
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ export const serialize = (obj: any) => {
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
if (obj[p] instanceof Array) {
|
||||
for (let i = 0, n = obj[p].length; i < n; i++) {
|
||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p][i]))
|
||||
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p][i]))
|
||||
}
|
||||
} else {
|
||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]))
|
||||
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]))
|
||||
}
|
||||
}
|
||||
}
|
||||
return str.join("&")
|
||||
return str.join('&')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user