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