From 5f5d84da87321747d35e9ce49b5b8e74ea15f71d Mon Sep 17 00:00:00 2001 From: Krishna Acondy Date: Wed, 16 Sep 2020 08:49:18 +0100 Subject: [PATCH] feat(job-execution): support absolute paths to SAS jobs --- src/SASViyaApiClient.ts | 92 ++++++++++++++++++++++++++++--------- src/SASjs.ts | 2 - src/SessionManager.ts | 2 +- src/utils/index.ts | 5 +- src/utils/isRelativePath.ts | 2 + 5 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 src/utils/isRelativePath.ts diff --git a/src/SASViyaApiClient.ts b/src/SASViyaApiClient.ts index 95ca77e..e6eae1d 100644 --- a/src/SASViyaApiClient.ts +++ b/src/SASViyaApiClient.ts @@ -3,6 +3,7 @@ import { parseAndSubmitAuthorizeForm, convertToCSV, makeRequest, + isRelativePath, isUri, isUrl } from './utils' @@ -32,10 +33,6 @@ export class SASViyaApiClient { private setCsrfToken: (csrfToken: CsrfToken) => void, private rootFolderMap = new Map() ) { - if (!rootFolderName) { - throw new Error('Root folder must be provided.') - } - if (serverUrl) isUrl(serverUrl) } @@ -47,6 +44,7 @@ export class SASViyaApiClient { this.setCsrfToken ) private isForceDeploy: boolean = false + private folderMap = new Map() /** * Returns a map containing the directory structure in the currently set root folder. @@ -192,7 +190,7 @@ export class SASViyaApiClient { } const { result: contexts } = await this.request<{ items: Context[] }>( - `${this.serverUrl}/compute/contexts`, + `${this.serverUrl}/compute/contexts?limit=10000`, { headers } ) const executionContext = @@ -892,32 +890,56 @@ export class SASViyaApiClient { data?: any, accessToken?: string ) { - if (!this.rootFolder) { - await this.populateRootFolder(accessToken) - } - if (!this.rootFolder) { - console.error('Root folder was not found') - throw new Error('Root folder was not found') - } - if (!this.rootFolderMap.size) { - await this.populateRootFolderMap(accessToken) - } - if (!this.rootFolderMap.size) { - console.error(`The job ${sasJob} was not found in ${this.rootFolderName}`) + if (isRelativePath(sasJob) && !this.rootFolderName) { throw new Error( - `The job ${sasJob} was not found in ${this.rootFolderName}` + 'Relative paths cannot be used without specifying a root folder name' ) } + if (isRelativePath(sasJob)) { + if (!this.rootFolder) { + await this.populateRootFolder(accessToken) + } + if (!this.rootFolder) { + console.error('Root folder was not found') + throw new Error('Root folder was not found') + } + if (!this.rootFolderMap.size) { + await this.populateRootFolderMap(accessToken) + } + if (!this.rootFolderMap.size) { + console.error( + `The job ${sasJob} was not found in ${this.rootFolderName}` + ) + throw new Error( + `The job ${sasJob} was not found in ${this.rootFolderName}` + ) + } + } else { + const folderPathParts = sasJob.split('/') + folderPathParts.pop() + const folderPath = folderPathParts.join('/') + await this.populateFolderMap(folderPath, accessToken) + } + const headers: any = { 'Content-Type': 'application/json' } if (!!accessToken) { headers.Authorization = `Bearer ${accessToken}` } - const folderName = sasJob.split('/')[0] - const jobName = sasJob.split('/')[1] - const jobFolder = this.rootFolderMap.get(folderName) - const jobToExecute = jobFolder?.find((item) => item.name === jobName) + let jobToExecute + if (isRelativePath(sasJob)) { + const folderName = sasJob.split('/')[0] + const jobName = sasJob.split('/')[1] + const jobFolder = this.rootFolderMap.get(folderName) + jobToExecute = jobFolder?.find((item) => item.name === jobName) + } else { + const folderPathParts = sasJob.split('/') + const jobName = folderPathParts.pop() + const folderPath = folderPathParts.join('/') + const jobFolder = this.folderMap.get(folderPath) + jobToExecute = jobFolder?.find((item) => item.name === jobName) + } if (!jobToExecute) { throw new Error('Job was not found.') @@ -1100,6 +1122,32 @@ export class SASViyaApiClient { } } + private async populateFolderMap(folderPath: string, accessToken?: string) { + const url = '/folders/folders/@item?path=' + folderPath + const requestInfo: any = { + method: 'GET' + } + if (accessToken) { + requestInfo.headers = { Authorization: `Bearer ${accessToken}` } + } + const { result: folder } = await this.request( + `${this.serverUrl}${url}`, + requestInfo + ) + if (!folder) { + throw new Error( + `The path ${folderPath} does not exist on ${this.serverUrl}` + ) + } + const { result: members } = await this.request<{ items: any[] }>( + `${this.serverUrl}/folders/folders/${folder.id}/members`, + requestInfo + ) + + const itemsAtRoot = members.items + this.folderMap.set(folderPath, itemsAtRoot) + } + private async populateRootFolderMap(accessToken?: string) { const allItems = new Map() const url = '/folders/folders/@item?path=' + this.rootFolderName diff --git a/src/SASjs.ts b/src/SASjs.ts index 1818dec..ffcba28 100644 --- a/src/SASjs.ts +++ b/src/SASjs.ts @@ -515,8 +515,6 @@ export default class SASjs { ...config } - sasJob = sasJob.startsWith('/') ? sasJob.replace('/', '') : sasJob - if (config.serverType === ServerType.SASViya && config.contextName) { if (config.useComputeApi) { requestResponse = await this.executeJobViaComputeApi( diff --git a/src/SessionManager.ts b/src/SessionManager.ts index fc3eb56..848072d 100644 --- a/src/SessionManager.ts +++ b/src/SessionManager.ts @@ -78,7 +78,7 @@ export class SessionManager { if (!this.currentContext) { const { result: contexts } = await this.request<{ items: Context[] - }>(`${this.serverUrl}/compute/contexts`, { + }>(`${this.serverUrl}/compute/contexts?limit=10000`, { headers: this.getHeaders(accessToken) }) diff --git a/src/utils/index.ts b/src/utils/index.ts index cab090b..f966a98 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,6 +4,9 @@ export * from './convertToCsv' export * from './isAuthorizeFormRequired' export * from './isLoginRequired' export * from './isLoginSuccess' +export * from './isRelativePath' +export * from './isUri' +export * from './isUrl' export * from './makeRequest' export * from './needsRetry' export * from './parseAndSubmitAuthorizeForm' @@ -13,5 +16,3 @@ export * from './parseSasViyaLog' export * from './serialize' export * from './splitChunks' export * from './parseWeboutResponse' -export * from './isUri' -export * from './isUrl' diff --git a/src/utils/isRelativePath.ts b/src/utils/isRelativePath.ts new file mode 100644 index 0000000..400ec36 --- /dev/null +++ b/src/utils/isRelativePath.ts @@ -0,0 +1,2 @@ +export const isRelativePath = (uri: string): boolean => + !!uri && !uri.startsWith('/')