1
0
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:
Yury Shkoda
2021-09-28 10:38:29 +03:00
parent 2f1d403af4
commit 7e64819eb2
5 changed files with 148 additions and 5 deletions

105
src/SASBaseApiClient.ts Normal file
View 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
})
}
}
}

View File

@@ -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,

View File

@@ -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 ''
} }
} }

View File

@@ -0,0 +1,8 @@
export interface ExecutionQuery {
_program: string
}
export interface ExecutionResult {
log: string
logPath: string
}

View File

@@ -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