@sasjs/adapter 3.3.1 → 3.5.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.
- package/.gitpod.yml +2 -0
- package/README.md +86 -8
- package/SASjs.d.ts +12 -12
- package/SASjsApiClient.d.ts +4 -2
- package/api/viya/spec/writeStream.spec.d.ts +1 -1
- package/index.js +2 -2
- package/jest.config.js +1 -1
- package/node/SASjs.d.ts +12 -12
- package/node/SASjsApiClient.d.ts +4 -2
- package/node/index.js +2 -2
- package/node/utils/convertToCsv.d.ts +5 -1
- package/node/utils/createAxiosInstance.d.ts +0 -1
- package/package.json +37 -37
- package/src/SASjs.ts +26 -8
- package/src/SASjsApiClient.ts +23 -3
- package/src/api/viya/executeScript.ts +8 -2
- package/src/api/viya/writeStream.ts +5 -9
- package/src/request/Sas9RequestClient.ts +2 -2
- package/src/types/system/global.d.ts +1 -0
- package/src/utils/convertToCsv.ts +108 -52
- package/src/utils/formatDataForRequest.ts +10 -1
- package/src/utils/parseWeboutResponse.ts +1 -0
- package/test/utils/formatDataForRequest.spec.d.ts +1 -0
- package/tsconfig.json +2 -1
- package/utils/convertToCsv.d.ts +5 -1
- package/utils/createAxiosInstance.d.ts +0 -1
|
@@ -2,4 +2,8 @@
|
|
|
2
2
|
* Converts the given JSON object array to a CSV string.
|
|
3
3
|
* @param data - the array of JSON objects to convert.
|
|
4
4
|
*/
|
|
5
|
-
export declare const convertToCSV: (data: any
|
|
5
|
+
export declare const convertToCSV: (data: any, sasFormats?: {
|
|
6
|
+
formats: {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
};
|
|
9
|
+
} | undefined) => string;
|
package/package.json
CHANGED
|
@@ -40,45 +40,45 @@
|
|
|
40
40
|
},
|
|
41
41
|
"license": "ISC",
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@types/axios": "
|
|
44
|
-
"@types/express": "
|
|
45
|
-
"@types/form-data": "
|
|
46
|
-
"@types/jest": "
|
|
47
|
-
"@types/mime": "
|
|
48
|
-
"@types/pem": "
|
|
49
|
-
"@types/tough-cookie": "
|
|
50
|
-
"copyfiles": "
|
|
51
|
-
"cp": "
|
|
52
|
-
"dotenv": "
|
|
53
|
-
"express": "
|
|
54
|
-
"jest": "
|
|
55
|
-
"jest-extended": "
|
|
56
|
-
"node-polyfill-webpack-plugin": "
|
|
57
|
-
"path": "
|
|
58
|
-
"pem": "
|
|
59
|
-
"process": "
|
|
60
|
-
"rimraf": "
|
|
61
|
-
"semantic-release": "
|
|
62
|
-
"terser-webpack-plugin": "
|
|
63
|
-
"ts-jest": "
|
|
64
|
-
"ts-loader": "
|
|
65
|
-
"tslint": "
|
|
66
|
-
"tslint-config-prettier": "
|
|
67
|
-
"typedoc": "0.
|
|
68
|
-
"typedoc-neo-theme": "
|
|
69
|
-
"typedoc-plugin-external-module-name": "
|
|
70
|
-
"typescript": "4.
|
|
71
|
-
"webpack": "
|
|
72
|
-
"webpack-cli": "
|
|
43
|
+
"@types/axios": "0.14.0",
|
|
44
|
+
"@types/express": "4.17.13",
|
|
45
|
+
"@types/form-data": "2.5.0",
|
|
46
|
+
"@types/jest": "27.0.2",
|
|
47
|
+
"@types/mime": "2.0.3",
|
|
48
|
+
"@types/pem": "1.9.6",
|
|
49
|
+
"@types/tough-cookie": "4.0.1",
|
|
50
|
+
"copyfiles": "2.4.1",
|
|
51
|
+
"cp": "0.2.0",
|
|
52
|
+
"dotenv": "10.0.0",
|
|
53
|
+
"express": "4.17.1",
|
|
54
|
+
"jest": "27.4.7",
|
|
55
|
+
"jest-extended": "2.0.0",
|
|
56
|
+
"node-polyfill-webpack-plugin": "1.1.4",
|
|
57
|
+
"path": "0.12.7",
|
|
58
|
+
"pem": "1.14.4",
|
|
59
|
+
"process": "0.11.10",
|
|
60
|
+
"rimraf": "3.0.2",
|
|
61
|
+
"semantic-release": "18.0.0",
|
|
62
|
+
"terser-webpack-plugin": "5.3.0",
|
|
63
|
+
"ts-jest": "27.1.3",
|
|
64
|
+
"ts-loader": "9.2.6",
|
|
65
|
+
"tslint": "6.1.3",
|
|
66
|
+
"tslint-config-prettier": "1.18.0",
|
|
67
|
+
"typedoc": "0.22.11",
|
|
68
|
+
"typedoc-neo-theme": "1.1.1",
|
|
69
|
+
"typedoc-plugin-external-module-name": "4.0.6",
|
|
70
|
+
"typescript": "4.5.4",
|
|
71
|
+
"webpack": "5.66.0",
|
|
72
|
+
"webpack-cli": "4.7.2"
|
|
73
73
|
},
|
|
74
74
|
"main": "index.js",
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@sasjs/utils": "
|
|
77
|
-
"axios": "
|
|
78
|
-
"axios-cookiejar-support": "
|
|
79
|
-
"form-data": "
|
|
80
|
-
"https": "
|
|
81
|
-
"tough-cookie": "
|
|
76
|
+
"@sasjs/utils": "2.35.0",
|
|
77
|
+
"axios": "0.26.0",
|
|
78
|
+
"axios-cookiejar-support": "2.0.3",
|
|
79
|
+
"form-data": "4.0.0",
|
|
80
|
+
"https": "1.0.0",
|
|
81
|
+
"tough-cookie": "4.0.0"
|
|
82
82
|
},
|
|
83
|
-
"version": "3.
|
|
83
|
+
"version": "3.5.0"
|
|
84
84
|
}
|
package/src/SASjs.ts
CHANGED
|
@@ -5,9 +5,8 @@ import {
|
|
|
5
5
|
EditContextInput,
|
|
6
6
|
PollOptions,
|
|
7
7
|
LoginMechanism,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ExecutionQuery
|
|
8
|
+
ExecutionQuery,
|
|
9
|
+
FileTree
|
|
11
10
|
} from './types'
|
|
12
11
|
import { SASViyaApiClient } from './SASViyaApiClient'
|
|
13
12
|
import { SAS9ApiClient } from './SAS9ApiClient'
|
|
@@ -736,15 +735,19 @@ export default class SASjs {
|
|
|
736
735
|
msg: string
|
|
737
736
|
} {
|
|
738
737
|
if (data === null) return { status: true, msg: '' }
|
|
738
|
+
|
|
739
|
+
const isSasFormatsTable = (key: string) =>
|
|
740
|
+
key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
|
|
741
|
+
|
|
739
742
|
for (const key in data) {
|
|
740
|
-
if (!key.match(/^[a-zA-Z_]/)) {
|
|
743
|
+
if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
|
|
741
744
|
return {
|
|
742
745
|
status: false,
|
|
743
746
|
msg: 'First letter of table should be alphabet or underscore.'
|
|
744
747
|
}
|
|
745
748
|
}
|
|
746
749
|
|
|
747
|
-
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
|
|
750
|
+
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
|
|
748
751
|
return { status: false, msg: 'Table name should be alphanumeric.' }
|
|
749
752
|
}
|
|
750
753
|
|
|
@@ -755,7 +758,7 @@ export default class SASjs {
|
|
|
755
758
|
}
|
|
756
759
|
}
|
|
757
760
|
|
|
758
|
-
if (this.getType(data[key]) !== 'Array') {
|
|
761
|
+
if (this.getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
|
|
759
762
|
return {
|
|
760
763
|
status: false,
|
|
761
764
|
msg: 'Parameter data contains invalid table structure.'
|
|
@@ -771,6 +774,7 @@ export default class SASjs {
|
|
|
771
774
|
}
|
|
772
775
|
}
|
|
773
776
|
}
|
|
777
|
+
|
|
774
778
|
return { status: true, msg: '' }
|
|
775
779
|
}
|
|
776
780
|
|
|
@@ -860,8 +864,22 @@ export default class SASjs {
|
|
|
860
864
|
)
|
|
861
865
|
}
|
|
862
866
|
|
|
863
|
-
|
|
864
|
-
|
|
867
|
+
/**
|
|
868
|
+
* Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
|
|
869
|
+
* @param members - the JSON specifying the folders and services to be created.
|
|
870
|
+
* @param appLoc - the base folder in which to create the new folders and
|
|
871
|
+
* services. If not provided, is taken from SASjsConfig.
|
|
872
|
+
* @param authConfig - a valid client, secret, refresh and access tokens that are authorised to execute compute jobs.
|
|
873
|
+
*/
|
|
874
|
+
public async deployToSASjs(
|
|
875
|
+
members: FileTree,
|
|
876
|
+
appLoc?: string,
|
|
877
|
+
authConfig?: AuthConfig
|
|
878
|
+
) {
|
|
879
|
+
if (!appLoc) {
|
|
880
|
+
appLoc = this.sasjsConfig.appLoc
|
|
881
|
+
}
|
|
882
|
+
return await this.sasJSApiClient?.deploy(members, appLoc, authConfig)
|
|
865
883
|
}
|
|
866
884
|
|
|
867
885
|
public async executeJobSASjs(query: ExecutionQuery) {
|
package/src/SASjsApiClient.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as https from 'https'
|
|
2
2
|
import { AxiosRequestConfig } from 'axios'
|
|
3
|
-
import axiosCookieJarSupport from 'axios-cookiejar-support'
|
|
3
|
+
import * as axiosCookieJarSupport from 'axios-cookiejar-support'
|
|
4
4
|
import * as tough from 'tough-cookie'
|
|
5
5
|
import { prefixMessage } from '@sasjs/utils/error'
|
|
6
6
|
import { RequestClient, throwIfError } from './RequestClient'
|
|
@@ -17,7 +17,7 @@ export class Sas9RequestClient extends RequestClient {
|
|
|
17
17
|
status >= 200 && status < 303
|
|
18
18
|
|
|
19
19
|
if (axiosCookieJarSupport) {
|
|
20
|
-
axiosCookieJarSupport(this.httpClient)
|
|
20
|
+
axiosCookieJarSupport.wrapper(this.httpClient)
|
|
21
21
|
this.httpClient.defaults.jar = new tough.CookieJar()
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import 'jest-extended'
|
|
@@ -2,70 +2,115 @@
|
|
|
2
2
|
* Converts the given JSON object array to a CSV string.
|
|
3
3
|
* @param data - the array of JSON objects to convert.
|
|
4
4
|
*/
|
|
5
|
-
export const convertToCSV = (
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
export const convertToCSV = (
|
|
6
|
+
data: any,
|
|
7
|
+
sasFormats?: { formats: { [key: string]: string } }
|
|
8
|
+
) => {
|
|
9
|
+
let formats = sasFormats?.formats
|
|
10
|
+
let headers: string[] = []
|
|
8
11
|
let csvTest
|
|
9
12
|
let invalidString = false
|
|
10
|
-
const
|
|
11
|
-
let firstFoundType: string | null = null
|
|
12
|
-
let hasMixedTypes: boolean = false
|
|
13
|
-
let rowNumError: number = -1
|
|
14
|
-
|
|
15
|
-
const longestValueForField = data
|
|
16
|
-
.map((row: any, index: number) => {
|
|
17
|
-
if (row[field] || row[field] === '') {
|
|
18
|
-
if (firstFoundType) {
|
|
19
|
-
let currentFieldType =
|
|
20
|
-
row[field] === '' || typeof row[field] === 'string'
|
|
21
|
-
? 'chars'
|
|
22
|
-
: 'number'
|
|
23
|
-
|
|
24
|
-
if (!hasMixedTypes) {
|
|
25
|
-
hasMixedTypes = currentFieldType !== firstFoundType
|
|
26
|
-
rowNumError = hasMixedTypes ? index + 1 : -1
|
|
27
|
-
}
|
|
28
|
-
} else {
|
|
29
|
-
if (row[field] === '') {
|
|
30
|
-
firstFoundType = 'chars'
|
|
31
|
-
} else {
|
|
32
|
-
firstFoundType =
|
|
33
|
-
typeof row[field] === 'string' ? 'chars' : 'number'
|
|
34
|
-
}
|
|
35
|
-
}
|
|
13
|
+
const specialMissingValueRegExp = /^[a-z_]{1}$/i
|
|
36
14
|
|
|
37
|
-
|
|
15
|
+
if (formats) {
|
|
16
|
+
headers = Object.keys(formats).map((key) => `${key}:${formats![key]}`)
|
|
17
|
+
}
|
|
38
18
|
|
|
39
|
-
|
|
40
|
-
byteSize = getByteSize(row[field])
|
|
41
|
-
}
|
|
19
|
+
const headerFields = Object.keys(data[0])
|
|
42
20
|
|
|
43
|
-
|
|
21
|
+
headerFields.forEach((field) => {
|
|
22
|
+
if (!formats || !Object.keys(formats).includes(field)) {
|
|
23
|
+
let hasNullOrNumber = false
|
|
24
|
+
let hasSpecialMissingString = false
|
|
25
|
+
|
|
26
|
+
data.forEach((row: { [key: string]: any }) => {
|
|
27
|
+
if (row[field] === null || typeof row[field] === 'number') {
|
|
28
|
+
hasNullOrNumber = true
|
|
29
|
+
} else if (
|
|
30
|
+
typeof row[field] === 'string' &&
|
|
31
|
+
specialMissingValueRegExp.test(row[field])
|
|
32
|
+
) {
|
|
33
|
+
hasSpecialMissingString = true
|
|
44
34
|
}
|
|
45
35
|
})
|
|
46
|
-
.sort((a: number, b: number) => b - a)[0]
|
|
47
|
-
if (longestValueForField && longestValueForField > 32765) {
|
|
48
|
-
invalidString = true
|
|
49
|
-
}
|
|
50
|
-
if (hasMixedTypes) {
|
|
51
|
-
console.error(
|
|
52
|
-
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
36
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
37
|
+
if (hasNullOrNumber && hasSpecialMissingString) {
|
|
38
|
+
headers.push(`${field}:best.`)
|
|
39
|
+
|
|
40
|
+
if (!formats) formats = {}
|
|
41
|
+
|
|
42
|
+
formats[field] = 'best.'
|
|
43
|
+
} else {
|
|
44
|
+
let firstFoundType: string | null = null
|
|
45
|
+
let hasMixedTypes: boolean = false
|
|
46
|
+
let rowNumError: number = -1
|
|
47
|
+
|
|
48
|
+
const longestValueForField = data
|
|
49
|
+
.map((row: any, index: number) => {
|
|
50
|
+
if (row[field] || row[field] === '') {
|
|
51
|
+
if (firstFoundType) {
|
|
52
|
+
let currentFieldType =
|
|
53
|
+
row[field] === '' || typeof row[field] === 'string'
|
|
54
|
+
? 'chars'
|
|
55
|
+
: 'number'
|
|
56
|
+
|
|
57
|
+
if (!hasMixedTypes) {
|
|
58
|
+
hasMixedTypes = currentFieldType !== firstFoundType
|
|
59
|
+
rowNumError = hasMixedTypes ? index + 1 : -1
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
if (row[field] === '') {
|
|
63
|
+
firstFoundType = 'chars'
|
|
64
|
+
} else {
|
|
65
|
+
firstFoundType =
|
|
66
|
+
typeof row[field] === 'string' ? 'chars' : 'number'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let byteSize
|
|
71
|
+
|
|
72
|
+
if (typeof row[field] === 'string') {
|
|
73
|
+
byteSize = getByteSize(row[field])
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return byteSize
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
.sort((a: number, b: number) => b - a)[0]
|
|
80
|
+
|
|
81
|
+
if (longestValueForField && longestValueForField > 32765) {
|
|
82
|
+
invalidString = true
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (hasMixedTypes) {
|
|
86
|
+
console.error(
|
|
87
|
+
`Row (${rowNumError}), Column (${field}) has mixed types: ERROR`
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
headers.push(
|
|
92
|
+
`${field}:${firstFoundType === 'chars' ? '$char' : ''}${
|
|
93
|
+
longestValueForField
|
|
94
|
+
? longestValueForField
|
|
95
|
+
: firstFoundType === 'chars'
|
|
96
|
+
? '1'
|
|
97
|
+
: 'best'
|
|
98
|
+
}.`
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
63
102
|
})
|
|
64
103
|
|
|
65
|
-
if (
|
|
66
|
-
|
|
104
|
+
if (sasFormats) {
|
|
105
|
+
headers = headers.sort(
|
|
106
|
+
(a, b) =>
|
|
107
|
+
headerFields.indexOf(a.replace(/:.*/, '')) -
|
|
108
|
+
headerFields.indexOf(b.replace(/:.*/, ''))
|
|
109
|
+
)
|
|
67
110
|
}
|
|
68
111
|
|
|
112
|
+
if (invalidString) return 'ERROR: LARGE STRING LENGTH'
|
|
113
|
+
|
|
69
114
|
csvTest = data.map((row: any) => {
|
|
70
115
|
const fields = Object.keys(row).map((fieldName, index) => {
|
|
71
116
|
let value
|
|
@@ -76,6 +121,17 @@ export const convertToCSV = (data: any) => {
|
|
|
76
121
|
// stringify with replacer converts null values to empty strings
|
|
77
122
|
value = currentCell === null ? '' : currentCell
|
|
78
123
|
|
|
124
|
+
if (formats && formats[fieldName] === 'best.') {
|
|
125
|
+
if (value && !specialMissingValueRegExp.test(value)) {
|
|
126
|
+
console.log(`🤖[value]🤖`, value)
|
|
127
|
+
throw new Error(
|
|
128
|
+
'Special missing value can only be a single character from A to Z or _'
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return `.${value.toLowerCase()}`
|
|
133
|
+
}
|
|
134
|
+
|
|
79
135
|
// if there any present, it should have preceding (") for escaping
|
|
80
136
|
value = value.replace(/"/g, `""`)
|
|
81
137
|
|
|
@@ -7,9 +7,17 @@ export const formatDataForRequest = (data: any) => {
|
|
|
7
7
|
const result: any = {}
|
|
8
8
|
|
|
9
9
|
for (const tableName in data) {
|
|
10
|
+
if (
|
|
11
|
+
tableName.match(/^\$.*/) &&
|
|
12
|
+
Object.keys(data).includes(tableName.replace(/^\$/, ''))
|
|
13
|
+
) {
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
tableCounter++
|
|
11
18
|
sasjsTables.push(tableName)
|
|
12
|
-
const csv = convertToCSV(data[tableName])
|
|
19
|
+
const csv = convertToCSV(data[tableName], data[`$${tableName}`])
|
|
20
|
+
|
|
13
21
|
if (csv === 'ERROR: LARGE STRING LENGTH') {
|
|
14
22
|
throw new Error(
|
|
15
23
|
'The max length of a string value in SASjs is 32765 characters.'
|
|
@@ -27,6 +35,7 @@ export const formatDataForRequest = (data: any) => {
|
|
|
27
35
|
result[`sasjs${tableCounter}data`] = csv
|
|
28
36
|
}
|
|
29
37
|
}
|
|
38
|
+
|
|
30
39
|
result['sasjs_tables'] = sasjsTables.join(' ')
|
|
31
40
|
|
|
32
41
|
return result
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/tsconfig.json
CHANGED
package/utils/convertToCsv.d.ts
CHANGED
|
@@ -2,4 +2,8 @@
|
|
|
2
2
|
* Converts the given JSON object array to a CSV string.
|
|
3
3
|
* @param data - the array of JSON objects to convert.
|
|
4
4
|
*/
|
|
5
|
-
export declare const convertToCSV: (data: any
|
|
5
|
+
export declare const convertToCSV: (data: any, sasFormats?: {
|
|
6
|
+
formats: {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
};
|
|
9
|
+
} | undefined) => string;
|