mirror of
https://github.com/sasjs/adapter.git
synced 2026-01-05 03:30:05 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2827978fe5 | ||
|
|
541c19c1a4 | ||
|
|
c5e995f8d6 | ||
|
|
8bf36da566 | ||
|
|
6e23a0362f | ||
| a59d78bcf7 | |||
| 33d4ee92a7 | |||
| dadce3d4c9 | |||
|
|
b61cf34723 | ||
|
|
22445d1268 | ||
| 5beda6547a | |||
| bd49b3757a | |||
| 88f70a7966 | |||
| 89ff323206 | |||
| d4357d939e | |||
| 2fa3a353fa | |||
|
|
c5824a8a8d | ||
|
|
56a1960fff |
14
package-lock.json
generated
14
package-lock.json
generated
@@ -1005,11 +1005,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sasjs/utils": {
|
"@sasjs/utils": {
|
||||||
"version": "2.18.0",
|
"version": "2.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.20.1.tgz",
|
||||||
"integrity": "sha512-6VTbRP1KU0gGi1mSIHl+XyL9Vqk8rBW7a7VQOF6vzD+AVgfgYd0t76djAUCcA7Dos8NJXAoDUuah+iNvXJY+cw==",
|
"integrity": "sha512-Wer6RrGPowBgvgJ2Hdk2nrdA9mIsG4AKI50s/cEWKfzMnQRQVrCNmVUyZlM5I8/pZRzsMzwq7PLaxjAADYUCuQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/prompts": "^2.0.11",
|
"@types/prompts": "^2.0.13",
|
||||||
"chalk": "^4.1.1",
|
"chalk": "^4.1.1",
|
||||||
"cli-table": "^0.3.6",
|
"cli-table": "^0.3.6",
|
||||||
"consola": "^2.15.0",
|
"consola": "^2.15.0",
|
||||||
@@ -1276,6 +1276,12 @@
|
|||||||
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
|
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/mime": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/minimist": {
|
"@types/minimist": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz",
|
||||||
|
|||||||
@@ -39,11 +39,13 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^26.0.23",
|
||||||
|
"@types/mime": "^2.0.3",
|
||||||
"@types/tough-cookie": "^4.0.0",
|
"@types/tough-cookie": "^4.0.0",
|
||||||
"cp": "^0.2.0",
|
"cp": "^0.2.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"jest": "^27.0.4",
|
"jest": "^27.0.4",
|
||||||
"jest-extended": "^0.11.5",
|
"jest-extended": "^0.11.5",
|
||||||
|
"mime": "^2.5.2",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
@@ -62,7 +64,7 @@
|
|||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/utils": "^2.18.0",
|
"@sasjs/utils": "^2.20.1",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"axios-cookiejar-support": "^1.0.1",
|
"axios-cookiejar-support": "^1.0.1",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
|
|||||||
@@ -145,6 +145,33 @@ export const basicTests = (
|
|||||||
sasjsConfig.debug === false
|
sasjsConfig.debug === false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Request with extra attributes on JES approach',
|
||||||
|
description:
|
||||||
|
'Should complete successful request with extra attributes present in response',
|
||||||
|
test: async () => {
|
||||||
|
const config = {
|
||||||
|
useComputeApi: false
|
||||||
|
}
|
||||||
|
|
||||||
|
return await adapter.request(
|
||||||
|
'common/sendArr',
|
||||||
|
stringData,
|
||||||
|
config,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
['output', 'file', 'data']
|
||||||
|
)
|
||||||
|
},
|
||||||
|
assertion: (response: any) => {
|
||||||
|
const responseKeys: any = Object.keys(response)
|
||||||
|
return (
|
||||||
|
responseKeys.includes('file') &&
|
||||||
|
responseKeys.includes('output') &&
|
||||||
|
responseKeys.includes('data')
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Context,
|
Context,
|
||||||
ContextAllAttributes,
|
ContextAllAttributes,
|
||||||
Folder,
|
Folder,
|
||||||
|
File,
|
||||||
EditContextInput,
|
EditContextInput,
|
||||||
JobDefinition,
|
JobDefinition,
|
||||||
PollOptions
|
PollOptions
|
||||||
@@ -30,6 +31,7 @@ import { isAuthorizeFormRequired } from './auth/isAuthorizeFormRequired'
|
|||||||
import { RequestClient } from './request/RequestClient'
|
import { RequestClient } from './request/RequestClient'
|
||||||
import { SasAuthResponse, MacroVar } from '@sasjs/utils/types'
|
import { SasAuthResponse, MacroVar } from '@sasjs/utils/types'
|
||||||
import { prefixMessage } from '@sasjs/utils/error'
|
import { prefixMessage } from '@sasjs/utils/error'
|
||||||
|
import * as mime from 'mime'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A client for interfacing with the SAS Viya REST API.
|
* A client for interfacing with the SAS Viya REST API.
|
||||||
@@ -536,6 +538,53 @@ export class SASViyaApiClient {
|
|||||||
.then((res) => res.result)
|
.then((res) => res.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file. Path to or URI of the parent folder is required.
|
||||||
|
* @param fileName - the name of the new file.
|
||||||
|
* @param contentBuffer - the content of the new file in Buffer.
|
||||||
|
* @param parentFolderPath - the full path to the parent folder. If not
|
||||||
|
* provided, the parentFolderUri must be provided.
|
||||||
|
* @param parentFolderUri - the URI (eg /folders/folders/UUID) of the parent
|
||||||
|
* folder. If not provided, the parentFolderPath must be provided.
|
||||||
|
* @param accessToken - an access token for authorizing the request.
|
||||||
|
*/
|
||||||
|
public async createFile(
|
||||||
|
fileName: string,
|
||||||
|
contentBuffer: Buffer,
|
||||||
|
parentFolderPath?: string,
|
||||||
|
parentFolderUri?: string,
|
||||||
|
accessToken?: string
|
||||||
|
): Promise<File> {
|
||||||
|
if (!parentFolderPath && !parentFolderUri) {
|
||||||
|
throw new Error('Path or URI of the parent folder is required.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parentFolderUri && parentFolderPath) {
|
||||||
|
parentFolderUri = await this.getFolderUri(parentFolderPath, accessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: 'application/vnd.sas.file+json',
|
||||||
|
'Content-Disposition': `filename="${fileName}";`
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new NodeFormData()
|
||||||
|
formData.append('file', contentBuffer, fileName)
|
||||||
|
|
||||||
|
const mimeType =
|
||||||
|
mime.getType(fileName.match(/\.[0-9a-z]+$/i)?.[0] || '') ?? 'text/plain'
|
||||||
|
|
||||||
|
return (
|
||||||
|
await this.requestClient.post<File>(
|
||||||
|
`/files/files?parentFolderUri=${parentFolderUri}&typeDefName=file#rawUpload`,
|
||||||
|
formData,
|
||||||
|
accessToken,
|
||||||
|
'multipart/form-data; boundary=' + (formData as any)._boundary,
|
||||||
|
headers
|
||||||
|
)
|
||||||
|
).result
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a folder. Path to or URI of the parent folder is required.
|
* Creates a folder. Path to or URI of the parent folder is required.
|
||||||
* @param folderName - the name of the new folder.
|
* @param folderName - the name of the new folder.
|
||||||
|
|||||||
56
src/SASjs.ts
56
src/SASjs.ts
@@ -14,6 +14,7 @@ import {
|
|||||||
Sas9JobExecutor
|
Sas9JobExecutor
|
||||||
} from './job-execution'
|
} from './job-execution'
|
||||||
import { ErrorResponse } from './types/errors'
|
import { ErrorResponse } from './types/errors'
|
||||||
|
import { ExtraResponseAttributes } from '@sasjs/utils/types'
|
||||||
|
|
||||||
const defaultConfig: SASjsConfig = {
|
const defaultConfig: SASjsConfig = {
|
||||||
serverUrl: '',
|
serverUrl: '',
|
||||||
@@ -267,7 +268,7 @@ export default class SASjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a folder at SAS file system.
|
* Creates a folder in the logical SAS folder tree
|
||||||
* @param folderName - name of the folder to be created.
|
* @param folderName - name of the folder to be created.
|
||||||
* @param parentFolderPath - the full path (eg `/Public/example/myFolder`) of the parent folder.
|
* @param parentFolderPath - the full path (eg `/Public/example/myFolder`) of the parent folder.
|
||||||
* @param parentFolderUri - the URI of the parent folder.
|
* @param parentFolderUri - the URI of the parent folder.
|
||||||
@@ -299,6 +300,40 @@ export default class SASjs {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file in the logical SAS folder tree
|
||||||
|
* @param fileName - name of the file to be created.
|
||||||
|
* @param content - content of the file to be created.
|
||||||
|
* @param parentFolderPath - the full path (eg `/Public/example/myFolder`) of the parent folder.
|
||||||
|
* @param parentFolderUri - the URI of the parent folder.
|
||||||
|
* @param accessToken - the access token to authorizing the request.
|
||||||
|
* @param sasApiClient - a client for interfacing with SAS API.
|
||||||
|
*/
|
||||||
|
public async createFile(
|
||||||
|
fileName: string,
|
||||||
|
content: Buffer,
|
||||||
|
parentFolderPath: string,
|
||||||
|
parentFolderUri?: string,
|
||||||
|
accessToken?: string,
|
||||||
|
sasApiClient?: SASViyaApiClient
|
||||||
|
) {
|
||||||
|
if (sasApiClient)
|
||||||
|
return await sasApiClient.createFile(
|
||||||
|
fileName,
|
||||||
|
content,
|
||||||
|
parentFolderPath,
|
||||||
|
parentFolderUri,
|
||||||
|
accessToken
|
||||||
|
)
|
||||||
|
return await this.sasViyaApiClient!.createFile(
|
||||||
|
fileName,
|
||||||
|
content,
|
||||||
|
parentFolderPath,
|
||||||
|
parentFolderUri,
|
||||||
|
accessToken
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a folder from the SAS file system.
|
* Fetches a folder from the SAS file system.
|
||||||
* @param folderPath - path of the folder to be fetched.
|
* @param folderPath - path of the folder to be fetched.
|
||||||
@@ -540,13 +575,17 @@ export default class SASjs {
|
|||||||
* `await request(sasJobPath, data, config, () => setIsLoggedIn(false))`
|
* `await request(sasJobPath, data, config, () => setIsLoggedIn(false))`
|
||||||
* If you are not passing in any data and configuration, it will look like so:
|
* If you are not passing in any data and configuration, it will look like so:
|
||||||
* `await request(sasJobPath, {}, {}, () => setIsLoggedIn(false))`
|
* `await request(sasJobPath, {}, {}, () => setIsLoggedIn(false))`
|
||||||
|
* @param extraResponseAttributes - a array of predefined values that are used
|
||||||
|
* to provide extra attributes (same names as those values) to be added in response
|
||||||
|
* Supported values are declared in ExtraResponseAttributes type.
|
||||||
*/
|
*/
|
||||||
public async request(
|
public async request(
|
||||||
sasJob: string,
|
sasJob: string,
|
||||||
data: { [key: string]: any },
|
data: { [key: string]: any },
|
||||||
config: { [key: string]: any } = {},
|
config: { [key: string]: any } = {},
|
||||||
loginRequiredCallback?: () => any,
|
loginRequiredCallback?: () => any,
|
||||||
accessToken?: string
|
accessToken?: string,
|
||||||
|
extraResponseAttributes: ExtraResponseAttributes[] = []
|
||||||
) {
|
) {
|
||||||
config = {
|
config = {
|
||||||
...this.sasjsConfig,
|
...this.sasjsConfig,
|
||||||
@@ -568,7 +607,8 @@ export default class SASjs {
|
|||||||
data,
|
data,
|
||||||
config,
|
config,
|
||||||
loginRequiredCallback,
|
loginRequiredCallback,
|
||||||
accessToken
|
accessToken,
|
||||||
|
extraResponseAttributes
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
@@ -874,6 +914,16 @@ export default class SASjs {
|
|||||||
isForced
|
isForced
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
case 'file':
|
||||||
|
await this.createFile(
|
||||||
|
member.name,
|
||||||
|
member.code,
|
||||||
|
parentFolder,
|
||||||
|
undefined,
|
||||||
|
accessToken,
|
||||||
|
sasApiClient
|
||||||
|
)
|
||||||
|
break
|
||||||
case 'service':
|
case 'service':
|
||||||
await this.createJobDefinition(
|
await this.createJobDefinition(
|
||||||
member.name,
|
member.name,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
JobExecutionError,
|
JobExecutionError,
|
||||||
LoginRequiredError
|
LoginRequiredError
|
||||||
} from '../types/errors'
|
} from '../types/errors'
|
||||||
|
import { ExtraResponseAttributes } from '@sasjs/utils/types'
|
||||||
import { BaseJobExecutor } from './JobExecutor'
|
import { BaseJobExecutor } from './JobExecutor'
|
||||||
|
|
||||||
export class JesJobExecutor extends BaseJobExecutor {
|
export class JesJobExecutor extends BaseJobExecutor {
|
||||||
@@ -17,7 +18,8 @@ export class JesJobExecutor extends BaseJobExecutor {
|
|||||||
data: any,
|
data: any,
|
||||||
config: any,
|
config: any,
|
||||||
loginRequiredCallback?: any,
|
loginRequiredCallback?: any,
|
||||||
accessToken?: string
|
accessToken?: string,
|
||||||
|
extraResponseAttributes: ExtraResponseAttributes[] = []
|
||||||
) {
|
) {
|
||||||
const loginCallback = loginRequiredCallback || (() => Promise.resolve())
|
const loginCallback = loginRequiredCallback || (() => Promise.resolve())
|
||||||
|
|
||||||
@@ -30,10 +32,26 @@ export class JesJobExecutor extends BaseJobExecutor {
|
|||||||
data,
|
data,
|
||||||
accessToken
|
accessToken
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response: any) => {
|
||||||
this.appendRequest(response, sasJob, config.debug)
|
this.appendRequest(response, sasJob, config.debug)
|
||||||
|
|
||||||
resolve(response)
|
let responseObject = {}
|
||||||
|
|
||||||
|
if (extraResponseAttributes && extraResponseAttributes.length > 0) {
|
||||||
|
const extraAttributes = extraResponseAttributes.reduce(
|
||||||
|
(map: any, obj: any) => ((map[obj] = response[obj]), map),
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
responseObject = {
|
||||||
|
result: response.result,
|
||||||
|
...extraAttributes
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
responseObject = response.result
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(responseObject)
|
||||||
})
|
})
|
||||||
.catch(async (e: Error) => {
|
.catch(async (e: Error) => {
|
||||||
if (e instanceof JobExecutionError) {
|
if (e instanceof JobExecutionError) {
|
||||||
@@ -50,7 +68,9 @@ export class JesJobExecutor extends BaseJobExecutor {
|
|||||||
sasJob,
|
sasJob,
|
||||||
data,
|
data,
|
||||||
config,
|
config,
|
||||||
loginRequiredCallback
|
loginRequiredCallback,
|
||||||
|
accessToken,
|
||||||
|
extraResponseAttributes
|
||||||
).then(
|
).then(
|
||||||
(res: any) => {
|
(res: any) => {
|
||||||
resolve(res)
|
resolve(res)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ServerType } from '@sasjs/utils/types'
|
import { ServerType } from '@sasjs/utils/types'
|
||||||
import { SASjsRequest } from '../types'
|
import { SASjsRequest } from '../types'
|
||||||
|
import { ExtraResponseAttributes } from '@sasjs/utils/types'
|
||||||
import { asyncForEach, parseGeneratedCode, parseSourceCode } from '../utils'
|
import { asyncForEach, parseGeneratedCode, parseSourceCode } from '../utils'
|
||||||
|
|
||||||
export type ExecuteFunction = () => Promise<any>
|
export type ExecuteFunction = () => Promise<any>
|
||||||
@@ -10,7 +11,8 @@ export interface JobExecutor {
|
|||||||
data: any,
|
data: any,
|
||||||
config: any,
|
config: any,
|
||||||
loginRequiredCallback?: any,
|
loginRequiredCallback?: any,
|
||||||
accessToken?: string
|
accessToken?: string,
|
||||||
|
extraResponseAttributes?: ExtraResponseAttributes[]
|
||||||
) => Promise<any>
|
) => Promise<any>
|
||||||
resendWaitingRequests: () => Promise<void>
|
resendWaitingRequests: () => Promise<void>
|
||||||
getRequests: () => SASjsRequest[]
|
getRequests: () => SASjsRequest[]
|
||||||
@@ -28,7 +30,8 @@ export abstract class BaseJobExecutor implements JobExecutor {
|
|||||||
data: any,
|
data: any,
|
||||||
config: any,
|
config: any,
|
||||||
loginRequiredCallback?: any,
|
loginRequiredCallback?: any,
|
||||||
accessToken?: string | undefined
|
accessToken?: string | undefined,
|
||||||
|
extraResponseAttributes?: ExtraResponseAttributes[]
|
||||||
): Promise<any>
|
): Promise<any>
|
||||||
|
|
||||||
resendWaitingRequests = async () => {
|
resendWaitingRequests = async () => {
|
||||||
|
|||||||
8
src/types/File.ts
Normal file
8
src/types/File.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Link } from './Link'
|
||||||
|
|
||||||
|
export interface File {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
parentUri: string
|
||||||
|
links: Link[]
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from './Context'
|
export * from './Context'
|
||||||
export * from './CsrfToken'
|
export * from './CsrfToken'
|
||||||
export * from './Folder'
|
export * from './Folder'
|
||||||
|
export * from './File'
|
||||||
export * from './Job'
|
export * from './Job'
|
||||||
export * from './JobDefinition'
|
export * from './JobDefinition'
|
||||||
export * from './JobResult'
|
export * from './JobResult'
|
||||||
|
|||||||
Reference in New Issue
Block a user