1
0
mirror of https://github.com/sasjs/adapter.git synced 2025-12-11 09:24:35 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Allan Bowe
2827978fe5 Merge pull request #390 from sasjs/service-pack-with-file-resource
feat: create file resource while deploying service pack for viya
2021-06-13 14:52:09 +03:00
Saad Jutt
541c19c1a4 chore(merge): Merge branch 'service-pack-with-file-resource' of github.com:sasjs/adapter into service-pack-with-file-resource 2021-06-13 16:26:27 +05:00
Saad Jutt
c5e995f8d6 chore: TSDoc comments updated 2021-06-13 16:25:04 +05:00
Allan Bowe
8bf36da566 Merge branch 'master' into service-pack-with-file-resource 2021-06-13 11:56:54 +03:00
Yury Shkoda
6e23a0362f Merge pull request #411 from sasjs/issue-408
feat: select extra attributes in JES response
2021-06-10 19:38:16 +03:00
a59d78bcf7 chore(git): Merge branch 'master' into issue-408 2021-06-10 15:06:10 +02:00
33d4ee92a7 chore: updated utils and comment 2021-06-10 15:03:51 +02:00
dadce3d4c9 chore: added extra attributes type from @sasjs/utils 2021-06-10 14:22:31 +02:00
Saad Jutt
b61cf34723 chore(merge): Merge branch 'master' into service-pack-with-file-resource 2021-06-10 16:55:35 +05:00
Saad Jutt
22445d1268 fix: uploading file Buffer with FormData 2021-06-10 16:49:20 +05:00
5beda6547a Merge branch 'master' into issue-408 2021-06-09 13:09:59 +02:00
bd49b3757a chore(git): Merge branch 'master' into issue-408 2021-06-09 13:05:48 +02:00
88f70a7966 chore: merge 2021-06-08 17:01:41 +02:00
89ff323206 style: lint 2021-06-08 16:55:10 +02:00
d4357d939e test: extra attributes on JES 2021-06-08 16:54:46 +02:00
2fa3a353fa feat: select extra attributes in JES response 2021-06-08 13:25:08 +02:00
Saad Jutt
c5824a8a8d fix: using mime package to determine content-type 2021-05-30 23:47:31 +05:00
Saad Jutt
56a1960fff feat: create file resource while deploying service pack for viya 2021-05-30 05:58:17 +05:00
9 changed files with 180 additions and 14 deletions

14
package-lock.json generated
View File

@@ -1005,11 +1005,11 @@
}
},
"@sasjs/utils": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.18.0.tgz",
"integrity": "sha512-6VTbRP1KU0gGi1mSIHl+XyL9Vqk8rBW7a7VQOF6vzD+AVgfgYd0t76djAUCcA7Dos8NJXAoDUuah+iNvXJY+cw==",
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.20.1.tgz",
"integrity": "sha512-Wer6RrGPowBgvgJ2Hdk2nrdA9mIsG4AKI50s/cEWKfzMnQRQVrCNmVUyZlM5I8/pZRzsMzwq7PLaxjAADYUCuQ==",
"requires": {
"@types/prompts": "^2.0.11",
"@types/prompts": "^2.0.13",
"chalk": "^4.1.1",
"cli-table": "^0.3.6",
"consola": "^2.15.0",
@@ -1276,6 +1276,12 @@
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
"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": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz",

View File

@@ -39,11 +39,13 @@
"license": "ISC",
"devDependencies": {
"@types/jest": "^26.0.23",
"@types/mime": "^2.0.3",
"@types/tough-cookie": "^4.0.0",
"cp": "^0.2.0",
"dotenv": "^10.0.0",
"jest": "^27.0.4",
"jest-extended": "^0.11.5",
"mime": "^2.5.2",
"path": "^0.12.7",
"process": "^0.11.10",
"rimraf": "^3.0.2",
@@ -62,7 +64,7 @@
},
"main": "index.js",
"dependencies": {
"@sasjs/utils": "^2.18.0",
"@sasjs/utils": "^2.20.1",
"axios": "^0.21.1",
"axios-cookiejar-support": "^1.0.1",
"form-data": "^4.0.0",

View File

@@ -145,6 +145,33 @@ export const basicTests = (
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')
)
}
}
]
})

View File

@@ -12,6 +12,7 @@ import {
Context,
ContextAllAttributes,
Folder,
File,
EditContextInput,
JobDefinition,
PollOptions
@@ -30,6 +31,7 @@ import { isAuthorizeFormRequired } from './auth/isAuthorizeFormRequired'
import { RequestClient } from './request/RequestClient'
import { SasAuthResponse, MacroVar } from '@sasjs/utils/types'
import { prefixMessage } from '@sasjs/utils/error'
import * as mime from 'mime'
/**
* A client for interfacing with the SAS Viya REST API.
@@ -536,6 +538,53 @@ export class SASViyaApiClient {
.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.
* @param folderName - the name of the new folder.

View File

@@ -14,6 +14,7 @@ import {
Sas9JobExecutor
} from './job-execution'
import { ErrorResponse } from './types/errors'
import { ExtraResponseAttributes } from '@sasjs/utils/types'
const defaultConfig: SASjsConfig = {
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 parentFolderPath - the full path (eg `/Public/example/myFolder`) 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.
* @param folderPath - path of the folder to be fetched.
@@ -540,13 +575,17 @@ export default class SASjs {
* `await request(sasJobPath, data, config, () => setIsLoggedIn(false))`
* If you are not passing in any data and configuration, it will look like so:
* `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(
sasJob: string,
data: { [key: string]: any },
config: { [key: string]: any } = {},
loginRequiredCallback?: () => any,
accessToken?: string
accessToken?: string,
extraResponseAttributes: ExtraResponseAttributes[] = []
) {
config = {
...this.sasjsConfig,
@@ -568,7 +607,8 @@ export default class SASjs {
data,
config,
loginRequiredCallback,
accessToken
accessToken,
extraResponseAttributes
)
}
} else if (
@@ -874,6 +914,16 @@ export default class SASjs {
isForced
)
break
case 'file':
await this.createFile(
member.name,
member.code,
parentFolder,
undefined,
accessToken,
sasApiClient
)
break
case 'service':
await this.createJobDefinition(
member.name,

View File

@@ -5,6 +5,7 @@ import {
JobExecutionError,
LoginRequiredError
} from '../types/errors'
import { ExtraResponseAttributes } from '@sasjs/utils/types'
import { BaseJobExecutor } from './JobExecutor'
export class JesJobExecutor extends BaseJobExecutor {
@@ -17,7 +18,8 @@ export class JesJobExecutor extends BaseJobExecutor {
data: any,
config: any,
loginRequiredCallback?: any,
accessToken?: string
accessToken?: string,
extraResponseAttributes: ExtraResponseAttributes[] = []
) {
const loginCallback = loginRequiredCallback || (() => Promise.resolve())
@@ -30,10 +32,26 @@ export class JesJobExecutor extends BaseJobExecutor {
data,
accessToken
)
.then((response) => {
.then((response: any) => {
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) => {
if (e instanceof JobExecutionError) {
@@ -50,7 +68,9 @@ export class JesJobExecutor extends BaseJobExecutor {
sasJob,
data,
config,
loginRequiredCallback
loginRequiredCallback,
accessToken,
extraResponseAttributes
).then(
(res: any) => {
resolve(res)

View File

@@ -1,5 +1,6 @@
import { ServerType } from '@sasjs/utils/types'
import { SASjsRequest } from '../types'
import { ExtraResponseAttributes } from '@sasjs/utils/types'
import { asyncForEach, parseGeneratedCode, parseSourceCode } from '../utils'
export type ExecuteFunction = () => Promise<any>
@@ -10,7 +11,8 @@ export interface JobExecutor {
data: any,
config: any,
loginRequiredCallback?: any,
accessToken?: string
accessToken?: string,
extraResponseAttributes?: ExtraResponseAttributes[]
) => Promise<any>
resendWaitingRequests: () => Promise<void>
getRequests: () => SASjsRequest[]
@@ -28,7 +30,8 @@ export abstract class BaseJobExecutor implements JobExecutor {
data: any,
config: any,
loginRequiredCallback?: any,
accessToken?: string | undefined
accessToken?: string | undefined,
extraResponseAttributes?: ExtraResponseAttributes[]
): Promise<any>
resendWaitingRequests = async () => {

8
src/types/File.ts Normal file
View File

@@ -0,0 +1,8 @@
import { Link } from './Link'
export interface File {
id: string
name: string
parentUri: string
links: Link[]
}

View File

@@ -1,6 +1,7 @@
export * from './Context'
export * from './CsrfToken'
export * from './Folder'
export * from './File'
export * from './Job'
export * from './JobDefinition'
export * from './JobResult'