@@ -3,6 +3,7 @@ import papa from 'papaparse';
3
3
4
4
import { CsvImportError } from '../../../../../shared/domain/errors.js' ;
5
5
import { convertDateValue } from '../../../../../shared/infrastructure/utils/date-utils.js' ;
6
+ import { AggregateImportError } from '../../../domain/errors.js' ;
6
7
7
8
const ERRORS = {
8
9
ENCODING_NOT_SUPPORTED : 'ENCODING_NOT_SUPPORTED' ,
@@ -34,29 +35,52 @@ class CsvOrganizationLearnerParser {
34
35
constructor ( input , organizationId , columns , learnerSet ) {
35
36
this . _input = input ;
36
37
this . _organizationId = organizationId ;
38
+ this . _errors = [ ] ;
37
39
this . _columns = columns ;
38
40
this . learnerSet = learnerSet ;
41
+ this . _supportedErrors = [
42
+ 'min_length' ,
43
+ 'max_length' ,
44
+ 'length' ,
45
+ 'date_format' ,
46
+ 'email_format' ,
47
+ 'required' ,
48
+ 'bad_values' ,
49
+ ] ;
39
50
}
40
51
41
52
parse ( encoding ) {
42
- const { learnerLines, fields } = this . _parse ( encoding ) ;
43
-
44
53
if ( ! encoding ) {
45
- throw new CsvImportError ( ERRORS . ENCODING_NOT_SUPPORTED ) ;
54
+ this . _errors . push ( new CsvImportError ( ERRORS . ENCODING_NOT_SUPPORTED ) ) ;
46
55
}
47
56
57
+ this . throwHasErrors ( ) ;
58
+
59
+ const { learnerLines, fields } = this . _parse ( encoding ) ;
60
+
61
+ this . throwHasErrors ( ) ;
62
+
48
63
this . _checkColumns ( fields ) ;
64
+
65
+ this . throwHasErrors ( ) ;
66
+
49
67
learnerLines . forEach ( ( line , index ) => {
50
68
const learnerAttributes = this . _lineToOrganizationLearnerAttributes ( line ) ;
51
69
try {
52
70
this . learnerSet . addLearner ( learnerAttributes ) ;
53
- } catch ( err ) {
54
- this . _handleError ( err , index ) ;
71
+ } catch ( errors ) {
72
+ this . _handleValidationError ( errors , index ) ;
55
73
}
56
74
} ) ;
75
+
76
+ this . throwHasErrors ( ) ;
57
77
return this . learnerSet ;
58
78
}
59
79
80
+ throwHasErrors ( ) {
81
+ if ( this . _errors . length > 0 ) throw new AggregateImportError ( this . _errors ) ;
82
+ }
83
+
60
84
/**
61
85
* Identify which encoding has the given file.
62
86
* To check it, we decode and parse the first line of the file with supported encodings.
@@ -95,10 +119,12 @@ class CsvOrganizationLearnerParser {
95
119
if ( errors . length ) {
96
120
const hasDelimiterError = errors . some ( ( error ) => error . type === 'Delimiter' ) ;
97
121
if ( hasDelimiterError ) {
98
- throw new CsvImportError ( ERRORS . BAD_CSV_FORMAT ) ;
122
+ this . _errors . push ( new CsvImportError ( ERRORS . BAD_CSV_FORMAT ) ) ;
99
123
}
100
124
}
101
125
126
+ this . throwHasErrors ( ) ;
127
+
102
128
return { learnerLines, fields } ;
103
129
}
104
130
@@ -121,20 +147,22 @@ class CsvOrganizationLearnerParser {
121
147
122
148
_checkColumns ( parsedColumns ) {
123
149
// Required columns
124
- const missingMandatoryColumn = this . _columns
125
- . filter ( ( c ) => c . isRequired )
126
- . find ( ( c ) => ! parsedColumns . includes ( c . name ) ) ;
150
+ const mandatoryColumn = this . _columns . filter ( ( c ) => c . isRequired ) ;
127
151
128
- if ( missingMandatoryColumn ) {
129
- throw new CsvImportError ( ERRORS . HEADER_REQUIRED , { field : missingMandatoryColumn . name } ) ;
130
- }
152
+ mandatoryColumn . forEach ( ( colum ) => {
153
+ if ( ! parsedColumns . includes ( colum . name ) ) {
154
+ this . _errors . push ( new CsvImportError ( ERRORS . HEADER_REQUIRED , { field : colum . name } ) ) ;
155
+ }
156
+ } ) ;
131
157
132
158
// Expected columns
133
159
const acceptedColumns = this . _columns . map ( ( column ) => column . name ) ;
134
160
135
- if ( _atLeastOneParsedColumnDoesNotMatchAcceptedColumns ( parsedColumns , acceptedColumns ) ) {
136
- throw new CsvImportError ( ERRORS . HEADER_UNKNOWN ) ;
137
- }
161
+ const unknowColumns = parsedColumns . filter ( ( columnName ) => ! acceptedColumns . includes ( columnName ) ) ;
162
+
163
+ unknowColumns . forEach ( ( columnName ) => {
164
+ if ( columnName !== '' ) this . _errors . push ( new CsvImportError ( ERRORS . HEADER_UNKNOWN , { field : columnName } ) ) ;
165
+ } ) ;
138
166
}
139
167
140
168
_buildDateAttribute ( dateString ) {
@@ -147,41 +175,37 @@ class CsvOrganizationLearnerParser {
147
175
return convertedDate || dateString ;
148
176
}
149
177
150
- _handleError ( err , index ) {
151
- const column = this . _columns . find ( ( column ) => column . property === err . key ) ;
152
- const line = index + 2 ;
153
- const field = column . name ;
154
- if ( err . why === 'min_length' ) {
155
- throw new CsvImportError ( ERRORS . FIELD_MIN_LENGTH , { line, field, limit : err . limit } ) ;
156
- }
157
- if ( err . why === 'max_length' ) {
158
- throw new CsvImportError ( ERRORS . FIELD_MAX_LENGTH , { line, field, limit : err . limit } ) ;
159
- }
160
- if ( err . why === 'length' ) {
161
- throw new CsvImportError ( ERRORS . FIELD_LENGTH , { line, field, limit : err . limit } ) ;
162
- }
163
- if ( err . why === 'date_format' || err . why === 'not_a_date' ) {
164
- throw new CsvImportError ( ERRORS . FIELD_DATE_FORMAT , { line, field } ) ;
165
- }
166
- if ( err . why === 'email_format' ) {
167
- throw new CsvImportError ( ERRORS . FIELD_EMAIL_FORMAT , { line, field } ) ;
168
- }
169
- if ( err . why === 'required' ) {
170
- throw new CsvImportError ( ERRORS . FIELD_REQUIRED , { line, field } ) ;
171
- }
172
- if ( err . why === 'bad_values' ) {
173
- throw new CsvImportError ( ERRORS . FIELD_BAD_VALUES , { line, field, valids : err . valids } ) ;
174
- }
175
- throw err ;
176
- }
177
- }
178
+ _handleValidationError ( errors , index ) {
179
+ errors . forEach ( ( err ) => {
180
+ const column = this . _columns . find ( ( column ) => column . property === err . key ) ;
181
+ const line = index + 2 ;
182
+ const field = column . name ;
178
183
179
- function _atLeastOneParsedColumnDoesNotMatchAcceptedColumns ( parsedColumns , acceptedColumns ) {
180
- return parsedColumns . some ( ( parsedColumn ) => {
181
- if ( parsedColumn !== '' ) {
182
- return ! acceptedColumns . includes ( parsedColumn ) ;
183
- }
184
- } ) ;
184
+ if ( err . why === 'min_length' ) {
185
+ this . _errors . push ( new CsvImportError ( ERRORS . FIELD_MIN_LENGTH , { line, field, limit : err . limit } ) ) ;
186
+ }
187
+ if ( err . why === 'max_length' ) {
188
+ this . _errors . push ( new CsvImportError ( ERRORS . FIELD_MAX_LENGTH , { line, field, limit : err . limit } ) ) ;
189
+ }
190
+ if ( err . why === 'length' ) {
191
+ this . _errors . push ( new CsvImportError ( ERRORS . FIELD_LENGTH , { line, field, limit : err . limit } ) ) ;
192
+ }
193
+ if ( err . why === 'date_format' || err . why === 'not_a_date' ) {
194
+ this . _errors . push ( new CsvImportError ( ERRORS . FIELD_DATE_FORMAT , { line, field } ) ) ;
195
+ }
196
+ if ( err . why === 'email_format' ) {
197
+ this . _errors . push ( new CsvImportError ( ERRORS . FIELD_EMAIL_FORMAT , { line, field } ) ) ;
198
+ }
199
+ if ( err . why === 'required' ) {
200
+ this . _errors . push ( new CsvImportError ( ERRORS . FIELD_REQUIRED , { line, field } ) ) ;
201
+ }
202
+ if ( err . why === 'bad_values' ) {
203
+ this . _errors . push ( new CsvImportError ( ERRORS . FIELD_BAD_VALUES , { line, field, valids : err . valids } ) ) ;
204
+ }
205
+
206
+ if ( ! this . _supportedErrors . includes ( err . why ) ) this . _errors . push ( err ) ;
207
+ } ) ;
208
+ }
185
209
}
186
210
187
211
export { CsvOrganizationLearnerParser } ;
0 commit comments