@sasjs/adapter 3.4.1 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ import { AuthConfig, ExtraResponseAttributes, ServerType } from '@sasjs/utils/types';
2
+ import { RequestClient } from '../request/RequestClient';
3
+ import { BaseJobExecutor } from './JobExecutor';
4
+ export declare class SasJsJobExecutor extends BaseJobExecutor {
5
+ private jobsPath;
6
+ private requestClient;
7
+ constructor(serverUrl: string, serverType: ServerType, jobsPath: string, requestClient: RequestClient);
8
+ execute(sasJob: string, data: any, config: any, loginRequiredCallback?: any, authConfig?: AuthConfig, extraResponseAttributes?: ExtraResponseAttributes[]): Promise<unknown>;
9
+ private getRequestParams;
10
+ }
@@ -1,6 +1,7 @@
1
1
  export * from './ComputeJobExecutor';
2
+ export * from './FileUploader';
2
3
  export * from './JesJobExecutor';
3
4
  export * from './JobExecutor';
4
5
  export * from './Sas9JobExecutor';
6
+ export * from './SasJsJobExecutor';
5
7
  export * from './WebJobExecutor';
6
- export * from './FileUploader';
@@ -16,7 +16,7 @@ export declare class SASjsConfig {
16
16
  * The location of the STP Process Web Application. By default the adapter
17
17
  * will use '/SASjsApi/stp/execute' on SAS JS.
18
18
  */
19
- pathSASJS?: string;
19
+ pathSASJS: string;
20
20
  /**
21
21
  * The location of the Stored Process Web Application. By default the adapter
22
22
  * will use '/SASStoredProcess/do' on SAS 9.
@@ -1,3 +1,2 @@
1
- /// <reference types="node" />
2
1
  import * as https from 'https';
3
2
  export declare const createAxiosInstance: (baseURL: string, httpsAgent?: https.Agent | undefined) => import("axios").AxiosInstance;
package/package.json CHANGED
@@ -51,34 +51,34 @@
51
51
  "cp": "0.2.0",
52
52
  "dotenv": "10.0.0",
53
53
  "express": "4.17.1",
54
- "jest": "27.2.0",
55
- "jest-extended": "0.11.5",
54
+ "jest": "27.4.7",
55
+ "jest-extended": "2.0.0",
56
56
  "node-polyfill-webpack-plugin": "1.1.4",
57
57
  "path": "0.12.7",
58
58
  "pem": "1.14.4",
59
59
  "process": "0.11.10",
60
60
  "rimraf": "3.0.2",
61
61
  "semantic-release": "18.0.0",
62
- "terser-webpack-plugin": "5.2.4",
63
- "ts-jest": "27.0.3",
62
+ "terser-webpack-plugin": "5.3.0",
63
+ "ts-jest": "27.1.3",
64
64
  "ts-loader": "9.2.6",
65
65
  "tslint": "6.1.3",
66
66
  "tslint-config-prettier": "1.18.0",
67
- "typedoc": "0.19.2",
67
+ "typedoc": "0.22.11",
68
68
  "typedoc-neo-theme": "1.1.1",
69
69
  "typedoc-plugin-external-module-name": "4.0.6",
70
- "typescript": "4.3.5",
71
- "webpack": "5.56.0",
70
+ "typescript": "4.5.4",
71
+ "webpack": "5.66.0",
72
72
  "webpack-cli": "4.7.2"
73
73
  },
74
74
  "main": "index.js",
75
75
  "dependencies": {
76
- "@sasjs/utils": "2.32.0",
77
- "axios": "0.25.0",
76
+ "@sasjs/utils": "2.35.0",
77
+ "axios": "0.26.0",
78
78
  "axios-cookiejar-support": "1.0.1",
79
79
  "form-data": "4.0.0",
80
80
  "https": "1.0.0",
81
81
  "tough-cookie": "4.0.0"
82
82
  },
83
- "version": "3.4.1"
83
+ "version": "3.6.0"
84
84
  }
package/src/SASjs.ts CHANGED
@@ -5,9 +5,8 @@ import {
5
5
  EditContextInput,
6
6
  PollOptions,
7
7
  LoginMechanism,
8
- FolderMember,
9
- ServiceMember,
10
- ExecutionQuery
8
+ ExecutionQuery,
9
+ FileTree
11
10
  } from './types'
12
11
  import { SASViyaApiClient } from './SASViyaApiClient'
13
12
  import { SAS9ApiClient } from './SAS9ApiClient'
@@ -28,6 +27,7 @@ import {
28
27
  ComputeJobExecutor,
29
28
  JesJobExecutor,
30
29
  Sas9JobExecutor,
30
+ SasJsJobExecutor,
31
31
  FileUploader
32
32
  } from './job-execution'
33
33
  import { ErrorResponse } from './types/errors'
@@ -63,6 +63,7 @@ export default class SASjs {
63
63
  private computeJobExecutor: JobExecutor | null = null
64
64
  private jesJobExecutor: JobExecutor | null = null
65
65
  private sas9JobExecutor: JobExecutor | null = null
66
+ private sasJsJobExecutor: JobExecutor | null = null
66
67
 
67
68
  constructor(config?: Partial<SASjsConfig>) {
68
69
  this.sasjsConfig = {
@@ -77,6 +78,12 @@ export default class SASjs {
77
78
  return this.requestClient?.getCsrfToken(type)
78
79
  }
79
80
 
81
+ /**
82
+ * Executes the sas code against SAS9 server
83
+ * @param linesOfCode - lines of sas code from the file to run.
84
+ * @param username - a string representing the username.
85
+ * @param password - a string representing the password.
86
+ */
80
87
  public async executeScriptSAS9(
81
88
  linesOfCode: string[],
82
89
  userName: string,
@@ -91,6 +98,38 @@ export default class SASjs {
91
98
  )
92
99
  }
93
100
 
101
+ /**
102
+ * Executes the sas code against SASViya server
103
+ * @param fileName - name of the file to run. It will be converted to path to the file being submitted for execution.
104
+ * @param linesOfCode - lines of sas code from the file to run.
105
+ * @param contextName - context name on which code will be run on the server.
106
+ * @param authConfig - (optional) the access token, refresh token, client and secret for authorizing the request.
107
+ * @param debug - (optional) if true, global debug config will be overriden
108
+ */
109
+ public async executeScriptSASViya(
110
+ fileName: string,
111
+ linesOfCode: string[],
112
+ contextName: string,
113
+ authConfig?: AuthConfig,
114
+ debug?: boolean
115
+ ) {
116
+ this.isMethodSupported('executeScriptSASViya', [ServerType.SasViya])
117
+ if (!contextName) {
118
+ throw new Error(
119
+ 'Context name is undefined. Please set a `contextName` in your SASjs or override config.'
120
+ )
121
+ }
122
+
123
+ return await this.sasViyaApiClient!.executeScript(
124
+ fileName,
125
+ linesOfCode,
126
+ contextName,
127
+ authConfig,
128
+ null,
129
+ debug ? debug : this.sasjsConfig.debug
130
+ )
131
+ }
132
+
94
133
  /**
95
134
  * Gets compute contexts.
96
135
  * @param accessToken - an access token for an authorized user.
@@ -254,38 +293,6 @@ export default class SASjs {
254
293
  return await this.sasViyaApiClient!.createSession(contextName, accessToken)
255
294
  }
256
295
 
257
- /**
258
- * Executes the sas code against given sas server
259
- * @param fileName - name of the file to run. It will be converted to path to the file being submitted for execution.
260
- * @param linesOfCode - lines of sas code from the file to run.
261
- * @param contextName - context name on which code will be run on the server.
262
- * @param authConfig - (optional) the access token, refresh token, client and secret for authorizing the request.
263
- * @param debug - (optional) if true, global debug config will be overriden
264
- */
265
- public async executeScriptSASViya(
266
- fileName: string,
267
- linesOfCode: string[],
268
- contextName: string,
269
- authConfig?: AuthConfig,
270
- debug?: boolean
271
- ) {
272
- this.isMethodSupported('executeScriptSASViya', [ServerType.SasViya])
273
- if (!contextName) {
274
- throw new Error(
275
- 'Context name is undefined. Please set a `contextName` in your SASjs or override config.'
276
- )
277
- }
278
-
279
- return await this.sasViyaApiClient!.executeScript(
280
- fileName,
281
- linesOfCode,
282
- contextName,
283
- authConfig,
284
- null,
285
- debug ? debug : this.sasjsConfig.debug
286
- )
287
- }
288
-
289
296
  /**
290
297
  * Creates a folder in the logical SAS folder tree
291
298
  * @param folderName - name of the folder to be created.
@@ -676,7 +683,16 @@ export default class SASjs {
676
683
  const validationResult = this.validateInput(data)
677
684
 
678
685
  if (validationResult.status) {
679
- if (
686
+ if (config.serverType === ServerType.Sasjs) {
687
+ return await this.sasJsJobExecutor!.execute(
688
+ sasJob,
689
+ data,
690
+ config,
691
+ loginRequiredCallback,
692
+ authConfig,
693
+ extraResponseAttributes
694
+ )
695
+ } else if (
680
696
  config.serverType !== ServerType.Sas9 &&
681
697
  config.useComputeApi !== undefined &&
682
698
  config.useComputeApi !== null
@@ -865,8 +881,22 @@ export default class SASjs {
865
881
  )
866
882
  }
867
883
 
868
- public async deployToSASjs(members: [FolderMember, ServiceMember]) {
869
- return await this.sasJSApiClient?.deploy(members, this.sasjsConfig.appLoc)
884
+ /**
885
+ * Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
886
+ * @param members - the JSON specifying the folders and services to be created.
887
+ * @param appLoc - the base folder in which to create the new folders and
888
+ * services. If not provided, is taken from SASjsConfig.
889
+ * @param authConfig - a valid client, secret, refresh and access tokens that are authorised to execute compute jobs.
890
+ */
891
+ public async deployToSASjs(
892
+ members: FileTree,
893
+ appLoc?: string,
894
+ authConfig?: AuthConfig
895
+ ) {
896
+ if (!appLoc) {
897
+ appLoc = this.sasjsConfig.appLoc
898
+ }
899
+ return await this.sasJSApiClient?.deploy(members, appLoc, authConfig)
870
900
  }
871
901
 
872
902
  public async executeJobSASjs(query: ExecutionQuery) {
@@ -1018,7 +1048,7 @@ export default class SASjs {
1018
1048
  ? this.sasjsConfig.pathSASViya
1019
1049
  : this.sasjsConfig.serverType === ServerType.Sas9
1020
1050
  ? this.sasjsConfig.pathSAS9
1021
- : this.sasjsConfig.pathSASJS || ''
1051
+ : this.sasjsConfig.pathSASJS
1022
1052
 
1023
1053
  this.authManager = new AuthManager(
1024
1054
  this.sasjsConfig.serverUrl,
@@ -1083,6 +1113,13 @@ export default class SASjs {
1083
1113
  this.sasViyaApiClient!
1084
1114
  )
1085
1115
 
1116
+ this.sasJsJobExecutor = new SasJsJobExecutor(
1117
+ this.sasjsConfig.serverUrl,
1118
+ this.sasjsConfig.serverType!,
1119
+ this.jobsPath,
1120
+ this.requestClient
1121
+ )
1122
+
1086
1123
  this.sas9JobExecutor = new Sas9JobExecutor(
1087
1124
  this.sasjsConfig.serverUrl,
1088
1125
  this.sasjsConfig.serverType!,
@@ -1,8 +1,11 @@
1
- import { FolderMember, ServiceMember, ExecutionQuery } from './types'
1
+ import { AuthConfig, ServerType } from '@sasjs/utils/types'
2
+ import { FileTree, ExecutionQuery } from './types'
2
3
  import { RequestClient } from './request/RequestClient'
3
4
  import { getAccessTokenForSasjs } from './auth/getAccessTokenForSasjs'
4
5
  import { refreshTokensForSasjs } from './auth/refreshTokensForSasjs'
5
6
  import { getAuthCodeForSasjs } from './auth/getAuthCodeForSasjs'
7
+ import { parseWeboutResponse } from './utils'
8
+ import { getTokens } from './auth/getTokens'
6
9
 
7
10
  export class SASjsApiClient {
8
11
  constructor(
@@ -14,7 +17,19 @@ export class SASjsApiClient {
14
17
  if (serverUrl) this.serverUrl = serverUrl
15
18
  }
16
19
 
17
- public async deploy(members: [FolderMember, ServiceMember], appLoc: string) {
20
+ public async deploy(
21
+ members: FileTree,
22
+ appLoc: string,
23
+ authConfig?: AuthConfig
24
+ ) {
25
+ let access_token = (authConfig || {}).access_token
26
+ if (authConfig) {
27
+ ;({ access_token } = await getTokens(
28
+ this.requestClient,
29
+ authConfig,
30
+ ServerType.Sasjs
31
+ ))
32
+ }
18
33
  const { result } = await this.requestClient.post<{
19
34
  status: string
20
35
  message: string
@@ -22,7 +37,7 @@ export class SASjsApiClient {
22
37
  }>(
23
38
  'SASjsApi/drive/deploy',
24
39
  { fileTree: members, appLoc: appLoc },
25
- undefined
40
+ access_token
26
41
  )
27
42
 
28
43
  return Promise.resolve(result)
@@ -35,8 +50,13 @@ export class SASjsApiClient {
35
50
  log?: string
36
51
  logPath?: string
37
52
  error?: {}
53
+ _webout?: string
38
54
  }>('SASjsApi/stp/execute', query, undefined)
39
55
 
56
+ if (Object.keys(result).includes('_webout')) {
57
+ result._webout = parseWeboutResponse(result._webout!)
58
+ }
59
+
40
60
  return Promise.resolve(result)
41
61
  }
42
62
 
@@ -272,7 +272,13 @@ export async function executeScript(
272
272
 
273
273
  return { result: jobResult?.result, log }
274
274
  } catch (e) {
275
- if (e && e.status === 404) {
275
+ interface HttpError {
276
+ status: number
277
+ }
278
+
279
+ const error = e as HttpError
280
+
281
+ if (error.status === 404) {
276
282
  return executeScript(
277
283
  requestClient,
278
284
  sessionManager,
@@ -287,7 +293,7 @@ export async function executeScript(
287
293
  true
288
294
  )
289
295
  } else {
290
- throw prefixMessage(e, 'Error while executing script. ')
296
+ throw prefixMessage(e as Error, 'Error while executing script. ')
291
297
  }
292
298
  }
293
299
  }
@@ -3,13 +3,9 @@ import { WriteStream } from '../../types'
3
3
  export const writeStream = async (
4
4
  stream: WriteStream,
5
5
  content: string
6
- ): Promise<void> => {
7
- return new Promise((resolve, reject) => {
8
- stream.write(content + '\n', (e) => {
9
- if (e) {
10
- return reject(e)
11
- }
12
- return resolve()
13
- })
6
+ ): Promise<void> =>
7
+ stream.write(content + '\n', (e) => {
8
+ if (e) return Promise.reject(e)
9
+
10
+ return Promise.resolve()
14
11
  })
15
- }
@@ -0,0 +1,123 @@
1
+ import {
2
+ AuthConfig,
3
+ ExtraResponseAttributes,
4
+ ServerType
5
+ } from '@sasjs/utils/types'
6
+ import {
7
+ ErrorResponse,
8
+ JobExecutionError,
9
+ LoginRequiredError
10
+ } from '../types/errors'
11
+ import { RequestClient } from '../request/RequestClient'
12
+ import {
13
+ isRelativePath,
14
+ appendExtraResponseAttributes,
15
+ getValidJson
16
+ } from '../utils'
17
+ import { BaseJobExecutor } from './JobExecutor'
18
+ import { parseWeboutResponse } from '../utils/parseWeboutResponse'
19
+
20
+ export class SasJsJobExecutor extends BaseJobExecutor {
21
+ constructor(
22
+ serverUrl: string,
23
+ serverType: ServerType,
24
+ private jobsPath: string,
25
+ private requestClient: RequestClient
26
+ ) {
27
+ super(serverUrl, serverType)
28
+ }
29
+
30
+ async execute(
31
+ sasJob: string,
32
+ data: any,
33
+ config: any,
34
+ loginRequiredCallback?: any,
35
+ authConfig?: AuthConfig,
36
+ extraResponseAttributes: ExtraResponseAttributes[] = []
37
+ ) {
38
+ const loginCallback = loginRequiredCallback || (() => Promise.resolve())
39
+ const program = isRelativePath(sasJob)
40
+ ? config.appLoc
41
+ ? config.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
42
+ : sasJob
43
+ : sasJob
44
+ let apiUrl = `${config.serverUrl}${this.jobsPath}/?${'_program=' + program}`
45
+
46
+ const requestParams = this.getRequestParams(config)
47
+
48
+ const requestPromise = new Promise((resolve, reject) => {
49
+ this.requestClient!.post(
50
+ apiUrl,
51
+ { ...requestParams, ...data },
52
+ authConfig?.access_token
53
+ )
54
+ .then(async (res: any) => {
55
+ const resObj = {
56
+ result: res.result._webout,
57
+ log: res.result.log
58
+ }
59
+ this.requestClient!.appendRequest(resObj, sasJob, config.debug)
60
+
61
+ let jsonResponse = res.result
62
+
63
+ if (config.debug) {
64
+ const webout = parseWeboutResponse(res.result._webout, apiUrl)
65
+ jsonResponse = getValidJson(webout)
66
+ } else {
67
+ jsonResponse = getValidJson(res.result._webout)
68
+ }
69
+
70
+ const responseObject = appendExtraResponseAttributes(
71
+ { result: jsonResponse },
72
+ extraResponseAttributes
73
+ )
74
+ resolve(responseObject)
75
+ })
76
+ .catch(async (e: Error) => {
77
+ if (e instanceof JobExecutionError) {
78
+ this.requestClient!.appendRequest(e, sasJob, config.debug)
79
+ reject(new ErrorResponse(e?.message, e))
80
+ }
81
+
82
+ if (e instanceof LoginRequiredError) {
83
+ this.appendWaitingRequest(() => {
84
+ return this.execute(
85
+ sasJob,
86
+ data,
87
+ config,
88
+ loginRequiredCallback,
89
+ authConfig,
90
+ extraResponseAttributes
91
+ ).then(
92
+ (res: any) => {
93
+ resolve(res)
94
+ },
95
+ (err: any) => {
96
+ reject(err)
97
+ }
98
+ )
99
+ })
100
+
101
+ await loginCallback()
102
+ } else {
103
+ reject(new ErrorResponse(e?.message, e))
104
+ }
105
+ })
106
+ })
107
+
108
+ return requestPromise
109
+ }
110
+
111
+ private getRequestParams(config: any): any {
112
+ const requestParams: any = {}
113
+
114
+ if (config.debug) {
115
+ requestParams['_omittextlog'] = 'false'
116
+ requestParams['_omitsessionresults'] = 'false'
117
+
118
+ requestParams['_debug'] = 131
119
+ }
120
+
121
+ return requestParams
122
+ }
123
+ }
@@ -1,6 +1,7 @@
1
1
  export * from './ComputeJobExecutor'
2
+ export * from './FileUploader'
2
3
  export * from './JesJobExecutor'
3
4
  export * from './JobExecutor'
4
5
  export * from './Sas9JobExecutor'
6
+ export * from './SasJsJobExecutor'
5
7
  export * from './WebJobExecutor'
6
- export * from './FileUploader'
@@ -16,7 +16,7 @@ export class SASjsConfig {
16
16
  * The location of the STP Process Web Application. By default the adapter
17
17
  * will use '/SASjsApi/stp/execute' on SAS JS.
18
18
  */
19
- pathSASJS?: string = ''
19
+ pathSASJS: string = ''
20
20
  /**
21
21
  * The location of the Stored Process Web Application. By default the adapter
22
22
  * will use '/SASStoredProcess/do' on SAS 9.
@@ -0,0 +1 @@
1
+ import 'jest-extended'
@@ -10,6 +10,7 @@ export const parseWeboutResponse = (response: string, url?: string): string => {
10
10
  .split('>>weboutEND<<')[0]
11
11
  } catch (e) {
12
12
  if (url) throw new WeboutResponseError(url)
13
+
13
14
  sasResponse = ''
14
15
  console.error(e)
15
16
  }
package/tsconfig.json CHANGED
@@ -6,7 +6,8 @@
6
6
  "declaration": true,
7
7
  "outDir": "./build",
8
8
  "strict": true,
9
- "sourceMap": true
9
+ "sourceMap": true,
10
+ "typeRoots": ["./node_modules/@types", "./src/types/system"]
10
11
  },
11
12
  "include": ["src"],
12
13
  "exclude": ["node_modules"]
@@ -16,7 +16,7 @@ export declare class SASjsConfig {
16
16
  * The location of the STP Process Web Application. By default the adapter
17
17
  * will use '/SASjsApi/stp/execute' on SAS JS.
18
18
  */
19
- pathSASJS?: string;
19
+ pathSASJS: string;
20
20
  /**
21
21
  * The location of the Stored Process Web Application. By default the adapter
22
22
  * will use '/SASStoredProcess/do' on SAS 9.
@@ -1,3 +1,2 @@
1
- /// <reference types="node" />
2
1
  import * as https from 'https';
3
2
  export declare const createAxiosInstance: (baseURL: string, httpsAgent?: https.Agent | undefined) => import("axios").AxiosInstance;