@sasjs/adapter 3.10.9 → 3.10.12
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/README.md +2 -1
- package/SASjs.d.ts +0 -14
- package/index.js +2 -2
- package/node/SASjs.d.ts +0 -14
- package/node/index.js +2 -2
- package/node/types/errors/InvalidSASjsCsrfError.d.ts +3 -0
- package/node/utils/index.d.ts +7 -6
- package/node/utils/validateInput.d.ts +14 -0
- package/package.json +1 -1
- package/src/SASjs.ts +2 -70
- package/src/auth/AuthManager.ts +10 -2
- package/src/request/RequestClient.ts +18 -6
- package/src/types/errors/InvalidSASjsCsrfError.ts +9 -0
- package/src/utils/index.ts +7 -6
- package/src/utils/validateInput.ts +90 -0
- package/types/errors/InvalidSASjsCsrfError.d.ts +3 -0
- package/utils/index.d.ts +7 -6
- package/utils/{convertToCsv.spec.d.ts → spec/convertToCsv.spec.d.ts} +0 -0
- package/utils/spec/validateInput.spec.d.ts +1 -0
- package/utils/validateInput.d.ts +14 -0
- package/node/types/errors/InvalidCsrfError.d.ts +0 -3
- package/src/types/errors/InvalidCsrfError.ts +0 -9
- package/types/errors/InvalidCsrfError.d.ts +0 -3
package/node/utils/index.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
export * from './appendExtraResponseAttributes';
|
|
1
2
|
export * from './asyncForEach';
|
|
2
3
|
export * from './compareTimestamps';
|
|
3
4
|
export * from './convertToCsv';
|
|
4
5
|
export * from './createAxiosInstance';
|
|
5
6
|
export * from './delay';
|
|
7
|
+
export * from './fetchLogByChunks';
|
|
8
|
+
export * from './getValidJson';
|
|
6
9
|
export * from './isNode';
|
|
7
10
|
export * from './isRelativePath';
|
|
8
11
|
export * from './isUri';
|
|
9
12
|
export * from './isUrl';
|
|
10
13
|
export * from './needsRetry';
|
|
11
14
|
export * from './parseGeneratedCode';
|
|
12
|
-
export * from './parseSourceCode';
|
|
13
15
|
export * from './parseSasViyaLog';
|
|
16
|
+
export * from './parseSourceCode';
|
|
17
|
+
export * from './parseViyaDebugResponse';
|
|
18
|
+
export * from './parseWeboutResponse';
|
|
14
19
|
export * from './serialize';
|
|
15
20
|
export * from './splitChunks';
|
|
16
|
-
export * from './
|
|
17
|
-
export * from './fetchLogByChunks';
|
|
18
|
-
export * from './getValidJson';
|
|
19
|
-
export * from './parseViyaDebugResponse';
|
|
20
|
-
export * from './appendExtraResponseAttributes';
|
|
21
|
+
export * from './validateInput';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const MORE_INFO = "For more info see https://sasjs.io/sasjs-adapter/#request-response";
|
|
2
|
+
export declare const INVALID_TABLE_STRUCTURE: string;
|
|
3
|
+
/**
|
|
4
|
+
* This function validates the input data structure and table naming convention
|
|
5
|
+
*
|
|
6
|
+
* @param data A json object that contains one or more tables, it can also be null
|
|
7
|
+
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateInput: (data: {
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
} | null) => {
|
|
12
|
+
status: boolean;
|
|
13
|
+
msg: string;
|
|
14
|
+
};
|
package/package.json
CHANGED
package/src/SASjs.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compareTimestamps, asyncForEach } from './utils'
|
|
1
|
+
import { compareTimestamps, asyncForEach, validateInput } from './utils'
|
|
2
2
|
import {
|
|
3
3
|
SASjsConfig,
|
|
4
4
|
UploadFile,
|
|
@@ -686,7 +686,7 @@ export default class SASjs {
|
|
|
686
686
|
...config
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
-
const validationResult =
|
|
689
|
+
const validationResult = validateInput(data)
|
|
690
690
|
|
|
691
691
|
// status is true if the data passes validation checks above
|
|
692
692
|
if (validationResult.status) {
|
|
@@ -748,74 +748,6 @@ export default class SASjs {
|
|
|
748
748
|
}
|
|
749
749
|
}
|
|
750
750
|
|
|
751
|
-
/**
|
|
752
|
-
* This function validates the input data structure and table naming convention
|
|
753
|
-
*
|
|
754
|
-
* @param data A json object that contains one or more tables, it can also be null
|
|
755
|
-
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
756
|
-
*/
|
|
757
|
-
private validateInput(data: { [key: string]: any } | null): {
|
|
758
|
-
status: boolean
|
|
759
|
-
msg: string
|
|
760
|
-
} {
|
|
761
|
-
if (data === null) return { status: true, msg: '' }
|
|
762
|
-
|
|
763
|
-
const isSasFormatsTable = (key: string) =>
|
|
764
|
-
key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
|
|
765
|
-
|
|
766
|
-
for (const key in data) {
|
|
767
|
-
if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
|
|
768
|
-
return {
|
|
769
|
-
status: false,
|
|
770
|
-
msg: 'First letter of table should be alphabet or underscore.'
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
|
|
775
|
-
return { status: false, msg: 'Table name should be alphanumeric.' }
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
if (key.length > 32) {
|
|
779
|
-
return {
|
|
780
|
-
status: false,
|
|
781
|
-
msg: 'Maximum length for table name could be 32 characters.'
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
if (this.getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
|
|
786
|
-
return {
|
|
787
|
-
status: false,
|
|
788
|
-
msg: 'Parameter data contains invalid table structure.'
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
for (let i = 0; i < data[key].length; i++) {
|
|
793
|
-
if (this.getType(data[key][i]) !== 'object') {
|
|
794
|
-
return {
|
|
795
|
-
status: false,
|
|
796
|
-
msg: `Table ${key} contains invalid structure.`
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
return { status: true, msg: '' }
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
* this function returns the type of variable
|
|
807
|
-
*
|
|
808
|
-
* @param data it could be anything, like string, array, object etc.
|
|
809
|
-
* @returns a string which tells the type of input parameter
|
|
810
|
-
*/
|
|
811
|
-
private getType(data: any): string {
|
|
812
|
-
if (Array.isArray(data)) {
|
|
813
|
-
return 'Array'
|
|
814
|
-
} else {
|
|
815
|
-
return typeof data
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
751
|
/**
|
|
820
752
|
* Creates the folders and services at the given location `appLoc` on the given server `serverUrl`.
|
|
821
753
|
* @param serviceJson - the JSON specifying the folders and services to be created.
|
package/src/auth/AuthManager.ts
CHANGED
|
@@ -223,9 +223,17 @@ export class AuthManager {
|
|
|
223
223
|
|
|
224
224
|
private async getNewLoginForm() {
|
|
225
225
|
if (this.serverType === ServerType.Sasjs) {
|
|
226
|
-
// server will be sending CSRF
|
|
226
|
+
// server will be sending CSRF token in response,
|
|
227
|
+
// need to save in cookie so that,
|
|
227
228
|
// http client will use it automatically
|
|
228
|
-
return this.requestClient.get('/', undefined)
|
|
229
|
+
return this.requestClient.get('/', undefined).then(({ result }) => {
|
|
230
|
+
const cookie =
|
|
231
|
+
/<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/.exec(
|
|
232
|
+
result as string
|
|
233
|
+
)?.[1]
|
|
234
|
+
|
|
235
|
+
if (cookie) document.cookie = cookie
|
|
236
|
+
})
|
|
229
237
|
}
|
|
230
238
|
|
|
231
239
|
const { result: formResponse } = await this.requestClient.get<string>(
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
parseSourceCode,
|
|
20
20
|
createAxiosInstance
|
|
21
21
|
} from '../utils'
|
|
22
|
-
import {
|
|
22
|
+
import { InvalidSASjsCsrfError } from '../types/errors/InvalidSASjsCsrfError'
|
|
23
23
|
|
|
24
24
|
export interface HttpClient {
|
|
25
25
|
get<T>(
|
|
@@ -154,8 +154,12 @@ export class RequestClient implements HttpClient {
|
|
|
154
154
|
sasWork = log
|
|
155
155
|
}
|
|
156
156
|
} else if (response?.result) {
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
// We parse only if it's a string, otherwise it would throw error
|
|
158
|
+
if (typeof response.result === 'string') {
|
|
159
|
+
sourceCode = parseSourceCode(response.result)
|
|
160
|
+
generatedCode = parseGeneratedCode(response.result)
|
|
161
|
+
}
|
|
162
|
+
|
|
159
163
|
sasWork = response.result.WORK
|
|
160
164
|
}
|
|
161
165
|
}
|
|
@@ -499,12 +503,20 @@ export class RequestClient implements HttpClient {
|
|
|
499
503
|
throw e
|
|
500
504
|
}
|
|
501
505
|
|
|
502
|
-
if (e instanceof
|
|
503
|
-
// Fetching root
|
|
506
|
+
if (e instanceof InvalidSASjsCsrfError) {
|
|
507
|
+
// Fetching root and creating CSRF cookie
|
|
504
508
|
await this.httpClient
|
|
505
509
|
.get('/', {
|
|
506
510
|
withCredentials: true
|
|
507
511
|
})
|
|
512
|
+
.then((response) => {
|
|
513
|
+
const cookie =
|
|
514
|
+
/<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/.exec(
|
|
515
|
+
response.data
|
|
516
|
+
)?.[1]
|
|
517
|
+
|
|
518
|
+
if (cookie) document.cookie = cookie
|
|
519
|
+
})
|
|
508
520
|
.catch((err) => {
|
|
509
521
|
throw prefixMessage(err, 'Error while re-fetching CSRF token.')
|
|
510
522
|
})
|
|
@@ -615,7 +627,7 @@ export const throwIfError = (response: AxiosResponse) => {
|
|
|
615
627
|
typeof response.data === 'string' &&
|
|
616
628
|
response.data.toLowerCase() === 'invalid csrf token!'
|
|
617
629
|
) {
|
|
618
|
-
throw new
|
|
630
|
+
throw new InvalidSASjsCsrfError()
|
|
619
631
|
}
|
|
620
632
|
break
|
|
621
633
|
case 401:
|
package/src/utils/index.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
export * from './appendExtraResponseAttributes'
|
|
1
2
|
export * from './asyncForEach'
|
|
2
3
|
export * from './compareTimestamps'
|
|
3
4
|
export * from './convertToCsv'
|
|
4
5
|
export * from './createAxiosInstance'
|
|
5
6
|
export * from './delay'
|
|
7
|
+
export * from './fetchLogByChunks'
|
|
8
|
+
export * from './getValidJson'
|
|
6
9
|
export * from './isNode'
|
|
7
10
|
export * from './isRelativePath'
|
|
8
11
|
export * from './isUri'
|
|
9
12
|
export * from './isUrl'
|
|
10
13
|
export * from './needsRetry'
|
|
11
14
|
export * from './parseGeneratedCode'
|
|
12
|
-
export * from './parseSourceCode'
|
|
13
15
|
export * from './parseSasViyaLog'
|
|
16
|
+
export * from './parseSourceCode'
|
|
17
|
+
export * from './parseViyaDebugResponse'
|
|
18
|
+
export * from './parseWeboutResponse'
|
|
14
19
|
export * from './serialize'
|
|
15
20
|
export * from './splitChunks'
|
|
16
|
-
export * from './
|
|
17
|
-
export * from './fetchLogByChunks'
|
|
18
|
-
export * from './getValidJson'
|
|
19
|
-
export * from './parseViyaDebugResponse'
|
|
20
|
-
export * from './appendExtraResponseAttributes'
|
|
21
|
+
export * from './validateInput'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export const MORE_INFO =
|
|
2
|
+
'For more info see https://sasjs.io/sasjs-adapter/#request-response'
|
|
3
|
+
export const INVALID_TABLE_STRUCTURE = `Parameter data contains invalid table structure. ${MORE_INFO}`
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This function validates the input data structure and table naming convention
|
|
7
|
+
*
|
|
8
|
+
* @param data A json object that contains one or more tables, it can also be null
|
|
9
|
+
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
10
|
+
*/
|
|
11
|
+
export const validateInput = (
|
|
12
|
+
data: { [key: string]: any } | null
|
|
13
|
+
): {
|
|
14
|
+
status: boolean
|
|
15
|
+
msg: string
|
|
16
|
+
} => {
|
|
17
|
+
if (data === null) return { status: true, msg: '' }
|
|
18
|
+
|
|
19
|
+
if (getType(data) !== 'object') {
|
|
20
|
+
return {
|
|
21
|
+
status: false,
|
|
22
|
+
msg: INVALID_TABLE_STRUCTURE
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isSasFormatsTable = (key: string) =>
|
|
27
|
+
key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
|
|
28
|
+
|
|
29
|
+
for (const key in data) {
|
|
30
|
+
if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
|
|
31
|
+
return {
|
|
32
|
+
status: false,
|
|
33
|
+
msg: 'First letter of table should be alphabet or underscore.'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
|
|
38
|
+
return { status: false, msg: 'Table name should be alphanumeric.' }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (key.length > 32) {
|
|
42
|
+
return {
|
|
43
|
+
status: false,
|
|
44
|
+
msg: 'Maximum length for table name could be 32 characters.'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
|
|
49
|
+
return {
|
|
50
|
+
status: false,
|
|
51
|
+
msg: INVALID_TABLE_STRUCTURE
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const item of data[key]) {
|
|
56
|
+
if (getType(item) !== 'object') {
|
|
57
|
+
return {
|
|
58
|
+
status: false,
|
|
59
|
+
msg: `Table ${key} contains invalid structure. ${MORE_INFO}`
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
const attributes = Object.keys(item)
|
|
63
|
+
for (const attribute of attributes) {
|
|
64
|
+
if (item[attribute] === undefined) {
|
|
65
|
+
return {
|
|
66
|
+
status: false,
|
|
67
|
+
msg: `A row in table ${key} contains invalid value. Can't assign undefined to ${attribute}.`
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { status: true, msg: '' }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* this function returns the type of variable
|
|
80
|
+
*
|
|
81
|
+
* @param data it could be anything, like string, array, object etc.
|
|
82
|
+
* @returns a string which tells the type of input parameter
|
|
83
|
+
*/
|
|
84
|
+
const getType = (data: any): string => {
|
|
85
|
+
if (Array.isArray(data)) {
|
|
86
|
+
return 'Array'
|
|
87
|
+
} else {
|
|
88
|
+
return typeof data
|
|
89
|
+
}
|
|
90
|
+
}
|
package/utils/index.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
export * from './appendExtraResponseAttributes';
|
|
1
2
|
export * from './asyncForEach';
|
|
2
3
|
export * from './compareTimestamps';
|
|
3
4
|
export * from './convertToCsv';
|
|
4
5
|
export * from './createAxiosInstance';
|
|
5
6
|
export * from './delay';
|
|
7
|
+
export * from './fetchLogByChunks';
|
|
8
|
+
export * from './getValidJson';
|
|
6
9
|
export * from './isNode';
|
|
7
10
|
export * from './isRelativePath';
|
|
8
11
|
export * from './isUri';
|
|
9
12
|
export * from './isUrl';
|
|
10
13
|
export * from './needsRetry';
|
|
11
14
|
export * from './parseGeneratedCode';
|
|
12
|
-
export * from './parseSourceCode';
|
|
13
15
|
export * from './parseSasViyaLog';
|
|
16
|
+
export * from './parseSourceCode';
|
|
17
|
+
export * from './parseViyaDebugResponse';
|
|
18
|
+
export * from './parseWeboutResponse';
|
|
14
19
|
export * from './serialize';
|
|
15
20
|
export * from './splitChunks';
|
|
16
|
-
export * from './
|
|
17
|
-
export * from './fetchLogByChunks';
|
|
18
|
-
export * from './getValidJson';
|
|
19
|
-
export * from './parseViyaDebugResponse';
|
|
20
|
-
export * from './appendExtraResponseAttributes';
|
|
21
|
+
export * from './validateInput';
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const MORE_INFO = "For more info see https://sasjs.io/sasjs-adapter/#request-response";
|
|
2
|
+
export declare const INVALID_TABLE_STRUCTURE: string;
|
|
3
|
+
/**
|
|
4
|
+
* This function validates the input data structure and table naming convention
|
|
5
|
+
*
|
|
6
|
+
* @param data A json object that contains one or more tables, it can also be null
|
|
7
|
+
* @returns An object which contains two attributes: 1) status: boolean, 2) msg: string
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateInput: (data: {
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
} | null) => {
|
|
12
|
+
status: boolean;
|
|
13
|
+
msg: string;
|
|
14
|
+
};
|