@sasjs/adapter 3.3.1 → 3.4.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.
@@ -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) => string;
5
+ export declare const convertToCSV: (data: any, sasFormats?: {
6
+ formats: {
7
+ [key: string]: string;
8
+ };
9
+ } | undefined) => string;
package/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  "https": "^1.0.0",
81
81
  "tough-cookie": "^4.0.0"
82
82
  },
83
- "version": "3.3.1"
83
+ "version": "3.4.0"
84
84
  }
package/src/SASjs.ts CHANGED
@@ -736,15 +736,19 @@ export default class SASjs {
736
736
  msg: string
737
737
  } {
738
738
  if (data === null) return { status: true, msg: '' }
739
+
740
+ const isSasFormatsTable = (key: string) =>
741
+ key.match(/^\$.*/) && Object.keys(data).includes(key.replace(/^\$/, ''))
742
+
739
743
  for (const key in data) {
740
- if (!key.match(/^[a-zA-Z_]/)) {
744
+ if (!key.match(/^[a-zA-Z_]/) && !isSasFormatsTable(key)) {
741
745
  return {
742
746
  status: false,
743
747
  msg: 'First letter of table should be alphabet or underscore.'
744
748
  }
745
749
  }
746
750
 
747
- if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
751
+ if (!key.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) && !isSasFormatsTable(key)) {
748
752
  return { status: false, msg: 'Table name should be alphanumeric.' }
749
753
  }
750
754
 
@@ -755,7 +759,7 @@ export default class SASjs {
755
759
  }
756
760
  }
757
761
 
758
- if (this.getType(data[key]) !== 'Array') {
762
+ if (this.getType(data[key]) !== 'Array' && !isSasFormatsTable(key)) {
759
763
  return {
760
764
  status: false,
761
765
  msg: 'Parameter data contains invalid table structure.'
@@ -771,6 +775,7 @@ export default class SASjs {
771
775
  }
772
776
  }
773
777
  }
778
+
774
779
  return { status: true, msg: '' }
775
780
  }
776
781
 
@@ -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 = (data: any) => {
6
- const replacer = (key: any, value: any) => (value === null ? '' : value)
7
- const headerFields = Object.keys(data[0])
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 headers = headerFields.map((field) => {
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
- let byteSize
15
+ if (formats) {
16
+ headers = Object.keys(formats).map((key) => `${key}:${formats![key]}`)
17
+ }
38
18
 
39
- if (typeof row[field] === 'string') {
40
- byteSize = getByteSize(row[field])
41
- }
19
+ const headerFields = Object.keys(data[0])
42
20
 
43
- return byteSize
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
- return `${field}:${firstFoundType === 'chars' ? '$char' : ''}${
57
- longestValueForField
58
- ? longestValueForField
59
- : firstFoundType === 'chars'
60
- ? '1'
61
- : 'best'
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 (invalidString) {
66
- return 'ERROR: LARGE STRING LENGTH'
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 {};
@@ -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) => string;
5
+ export declare const convertToCSV: (data: any, sasFormats?: {
6
+ formats: {
7
+ [key: string]: string;
8
+ };
9
+ } | undefined) => string;