mirror of
https://github.com/sasjs/adapter.git
synced 2025-12-11 09:24:35 +00:00
feat(sasjs/server): add SASBaseApiClient class
This commit is contained in:
105
src/SASBaseApiClient.ts
Normal file
105
src/SASBaseApiClient.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import {
|
||||||
|
MemberType,
|
||||||
|
FolderMember,
|
||||||
|
ServiceMember,
|
||||||
|
ExecutionQuery,
|
||||||
|
ExecutionResult
|
||||||
|
} from './types'
|
||||||
|
import {
|
||||||
|
createFolder,
|
||||||
|
createFile,
|
||||||
|
fileExists,
|
||||||
|
readFile,
|
||||||
|
asyncForEach,
|
||||||
|
generateTimestamp
|
||||||
|
} from '@sasjs/utils'
|
||||||
|
import { getTmpFilesFolderPath, getTmpLogFolderPath } from './utils'
|
||||||
|
import * as path from 'path'
|
||||||
|
import { promisify } from 'util'
|
||||||
|
import { execFile } from 'child_process'
|
||||||
|
const execFilePromise = promisify(execFile)
|
||||||
|
|
||||||
|
export class SASBaseApiClient {
|
||||||
|
public async deploy(members: [FolderMember, ServiceMember]) {
|
||||||
|
await this.createFileTree(members)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private pathSASBase: string) {}
|
||||||
|
|
||||||
|
public setConfig(pathSASBase: string) {
|
||||||
|
if (pathSASBase) this.pathSASBase = pathSASBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make public
|
||||||
|
private async createFileTree(
|
||||||
|
members: [FolderMember, ServiceMember],
|
||||||
|
parentFolders: string[] = []
|
||||||
|
) {
|
||||||
|
const destinationPath = path.join(
|
||||||
|
await getTmpFilesFolderPath(),
|
||||||
|
path.join(...parentFolders)
|
||||||
|
)
|
||||||
|
|
||||||
|
await asyncForEach(
|
||||||
|
members,
|
||||||
|
async (member: FolderMember | ServiceMember) => {
|
||||||
|
const name = member.name
|
||||||
|
|
||||||
|
if (member.type === MemberType.folder) {
|
||||||
|
await createFolder(path.join(destinationPath, name)).catch((err) =>
|
||||||
|
Promise.reject({ error: err, failedToCreate: name })
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.createFileTree(member.members, [
|
||||||
|
...parentFolders,
|
||||||
|
name
|
||||||
|
]).catch((err) =>
|
||||||
|
Promise.reject({ error: err, failedToCreate: name })
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await createFile(path.join(destinationPath, name), member.code).catch(
|
||||||
|
(err) => Promise.reject({ error: err, failedToCreate: name })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return Promise.resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async executeScript(
|
||||||
|
query: ExecutionQuery
|
||||||
|
): Promise<ExecutionResult | undefined> {
|
||||||
|
let sasCodePath = path.join(await getTmpFilesFolderPath(), query._program)
|
||||||
|
|
||||||
|
sasCodePath = sasCodePath.replace(new RegExp('/', 'g'), path.sep)
|
||||||
|
|
||||||
|
if (!(await fileExists(sasCodePath))) {
|
||||||
|
return Promise.reject(`${query._program} does not exist.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sasFile: string = sasCodePath.split(path.sep).pop() || 'default'
|
||||||
|
|
||||||
|
const sasLogPath = path.join(
|
||||||
|
await getTmpLogFolderPath(),
|
||||||
|
[sasFile.replace(/\.sas/g, ''), '-', generateTimestamp(), '.log'].join('')
|
||||||
|
)
|
||||||
|
|
||||||
|
const { stdout, stderr } = await execFilePromise(this.pathSASBase, [
|
||||||
|
'-SYSIN',
|
||||||
|
sasCodePath,
|
||||||
|
'-log',
|
||||||
|
sasLogPath,
|
||||||
|
'-nosplash'
|
||||||
|
])
|
||||||
|
|
||||||
|
if (stderr) return Promise.reject(stderr)
|
||||||
|
|
||||||
|
if (await fileExists(sasLogPath)) {
|
||||||
|
return Promise.resolve({
|
||||||
|
log: await readFile(sasLogPath),
|
||||||
|
logPath: sasLogPath
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/SASjs.ts
37
src/SASjs.ts
@@ -4,10 +4,14 @@ import {
|
|||||||
UploadFile,
|
UploadFile,
|
||||||
EditContextInput,
|
EditContextInput,
|
||||||
PollOptions,
|
PollOptions,
|
||||||
LoginMechanism
|
LoginMechanism,
|
||||||
|
FolderMember,
|
||||||
|
ServiceMember,
|
||||||
|
ExecutionQuery
|
||||||
} from './types'
|
} from './types'
|
||||||
import { SASViyaApiClient } from './SASViyaApiClient'
|
import { SASViyaApiClient } from './SASViyaApiClient'
|
||||||
import { SAS9ApiClient } from './SAS9ApiClient'
|
import { SAS9ApiClient } from './SAS9ApiClient'
|
||||||
|
import { SASBaseApiClient } from './SASBaseApiClient'
|
||||||
import { AuthManager } from './auth'
|
import { AuthManager } from './auth'
|
||||||
import {
|
import {
|
||||||
ServerType,
|
ServerType,
|
||||||
@@ -29,6 +33,7 @@ import { LoginOptions, LoginResult } from './types/Login'
|
|||||||
|
|
||||||
const defaultConfig: SASjsConfig = {
|
const defaultConfig: SASjsConfig = {
|
||||||
serverUrl: '',
|
serverUrl: '',
|
||||||
|
pathSASBase: '',
|
||||||
pathSAS9: '/SASStoredProcess/do',
|
pathSAS9: '/SASStoredProcess/do',
|
||||||
pathSASViya: '/SASJobExecution',
|
pathSASViya: '/SASJobExecution',
|
||||||
appLoc: '/Public/seedapp',
|
appLoc: '/Public/seedapp',
|
||||||
@@ -49,6 +54,7 @@ export default class SASjs {
|
|||||||
private jobsPath: string = ''
|
private jobsPath: string = ''
|
||||||
private sasViyaApiClient: SASViyaApiClient | null = null
|
private sasViyaApiClient: SASViyaApiClient | null = null
|
||||||
private sas9ApiClient: SAS9ApiClient | null = null
|
private sas9ApiClient: SAS9ApiClient | null = null
|
||||||
|
private sasBaseApiClient: SASBaseApiClient | null = null
|
||||||
private fileUploader: FileUploader | null = null
|
private fileUploader: FileUploader | null = null
|
||||||
private authManager: AuthManager | null = null
|
private authManager: AuthManager | null = null
|
||||||
private requestClient: RequestClient | null = null
|
private requestClient: RequestClient | null = null
|
||||||
@@ -824,6 +830,14 @@ export default class SASjs {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async deployToSASBase(members: [FolderMember, ServiceMember]) {
|
||||||
|
return await this.sasBaseApiClient?.deploy(members)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async executeScriptSASBase(query: ExecutionQuery) {
|
||||||
|
return await this.sasBaseApiClient?.executeScript(query)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kicks off execution of the given job via the compute API.
|
* Kicks off execution of the given job via the compute API.
|
||||||
* @returns an object representing the compute session created for the given job.
|
* @returns an object representing the compute session created for the given job.
|
||||||
@@ -973,31 +987,44 @@ export default class SASjs {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (this.sasjsConfig.serverType === ServerType.SasViya) {
|
if (this.sasjsConfig.serverType === ServerType.SasViya) {
|
||||||
if (this.sasViyaApiClient)
|
if (this.sasViyaApiClient) {
|
||||||
this.sasViyaApiClient!.setConfig(
|
this.sasViyaApiClient!.setConfig(
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.sasjsConfig.appLoc
|
this.sasjsConfig.appLoc
|
||||||
)
|
)
|
||||||
else
|
} else {
|
||||||
this.sasViyaApiClient = new SASViyaApiClient(
|
this.sasViyaApiClient = new SASViyaApiClient(
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.sasjsConfig.appLoc,
|
this.sasjsConfig.appLoc,
|
||||||
this.sasjsConfig.contextName,
|
this.sasjsConfig.contextName,
|
||||||
this.requestClient
|
this.requestClient
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
this.sasViyaApiClient.debug = this.sasjsConfig.debug
|
this.sasViyaApiClient.debug = this.sasjsConfig.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sasjsConfig.serverType === ServerType.Sas9) {
|
if (this.sasjsConfig.serverType === ServerType.Sas9) {
|
||||||
if (this.sas9ApiClient)
|
if (this.sas9ApiClient) {
|
||||||
this.sas9ApiClient!.setConfig(this.sasjsConfig.serverUrl)
|
this.sas9ApiClient!.setConfig(this.sasjsConfig.serverUrl)
|
||||||
else
|
} else {
|
||||||
this.sas9ApiClient = new SAS9ApiClient(
|
this.sas9ApiClient = new SAS9ApiClient(
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
this.jobsPath,
|
this.jobsPath,
|
||||||
this.sasjsConfig.allowInsecureRequests
|
this.sasjsConfig.allowInsecureRequests
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sasjsConfig.serverType === ServerType.Sasjs) {
|
||||||
|
if (this.sasBaseApiClient) {
|
||||||
|
this.sasBaseApiClient.setConfig(this.sasjsConfig.pathSASBase)
|
||||||
|
} else {
|
||||||
|
this.sasBaseApiClient = new SASBaseApiClient(
|
||||||
|
this.sasjsConfig.pathSASBase
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.fileUploader = new FileUploader(
|
this.fileUploader = new FileUploader(
|
||||||
this.sasjsConfig.serverUrl,
|
this.sasjsConfig.serverUrl,
|
||||||
|
|||||||
@@ -256,6 +256,8 @@ export class AuthManager {
|
|||||||
.split(' ')
|
.split(' ')
|
||||||
.map((name: string) => name.slice(0, 3).toLowerCase())
|
.map((name: string) => name.slice(0, 3).toLowerCase())
|
||||||
.join('')
|
.join('')
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
8
src/types/ExecuteScript.ts
Normal file
8
src/types/ExecuteScript.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export interface ExecutionQuery {
|
||||||
|
_program: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExecutionResult {
|
||||||
|
log: string
|
||||||
|
logPath: string
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ export class SASjsConfig {
|
|||||||
* will use '/SASJobExecution' on SAS Viya.
|
* will use '/SASJobExecution' on SAS Viya.
|
||||||
*/
|
*/
|
||||||
pathSASViya: string = ''
|
pathSASViya: string = ''
|
||||||
|
pathSASBase: 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. We recommend that each app is stored in
|
* Execution Services) are stored. We recommend that each app is stored in
|
||||||
|
|||||||
Reference in New Issue
Block a user