ffc-pay-etl-framework 1.4.0 → 1.4.1-alpha.13
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/app/database_connections/index.js +7 -0
- package/app/database_connections/postgresDatabaseConnection.js +47 -0
- package/app/database_connections/providedConnection.js +19 -0
- package/app/destinations/README.md +107 -0
- package/app/destinations/consoleDestination.js +38 -0
- package/app/destinations/csvFileDestination.js +75 -0
- package/app/destinations/index.js +12 -0
- package/app/destinations/postgresDestination.js +139 -0
- package/app/destinations/sqlFileDestination.js +81 -0
- package/app/lib/index.js +106 -0
- package/app/lib/rowMetaData.js +24 -0
- package/app/loaders/csvloader.js +58 -0
- package/app/loaders/index.js +5 -0
- package/app/misc/README.md +55 -0
- package/app/misc/index.js +0 -0
- package/app/misc/postgresSQLTask.js +71 -0
- package/app/transformers/README.md +102 -0
- package/app/transformers/fakerTransformer.js +48 -0
- package/app/transformers/index.js +9 -0
- package/app/transformers/stringReplaceTransformer.js +35 -0
- package/app/transformers/toUpperCaseTransformer.js +27 -0
- package/app/validators/README.md +81 -0
- package/app/validators/index.js +9 -0
- package/app/validators/multiToolValidator.js +49 -0
- package/app/validators/requiredValidator.js +35 -0
- package/app/validators/uniqueValidator.js +41 -0
- package/examples/additional-tasks.js +127 -0
- package/examples/csv-faker.js +30 -0
- package/examples/csv-find-replace.js +97 -0
- package/examples/csv-multi-tool-validator.js +38 -0
- package/examples/csv-required-validator.js +27 -0
- package/examples/csv-to-csv.js +24 -0
- package/examples/csv-to-postgres.js +36 -0
- package/examples/csv-to-sqlfile.js +31 -0
- package/examples/csv-to-upper-case.js +25 -0
- package/examples/csv-unique-validator.js +27 -0
- package/examples/index.js +26 -0
- package/examples/sql-return-values.js +37 -0
- package/index.js +15 -0
- package/package.json +28 -44
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const { Transform } = require('stream')
|
|
4
|
+
const { parse } = require('csv-parse')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} options
|
|
9
|
+
* @param {String} [options.path]
|
|
10
|
+
* @param {ReadableStream} [options.stream]
|
|
11
|
+
* @param {Number} [options.startingLine]
|
|
12
|
+
* @param {Boolean} [options.relax]
|
|
13
|
+
* @returns StreamReader
|
|
14
|
+
*/
|
|
15
|
+
function CSVLoader (options) {
|
|
16
|
+
let csvLoader
|
|
17
|
+
if (options.path) {
|
|
18
|
+
csvLoader = fs.createReadStream(options.path)
|
|
19
|
+
} else {
|
|
20
|
+
csvLoader = options.stream
|
|
21
|
+
}
|
|
22
|
+
let lineCount = 1
|
|
23
|
+
const fromLine = options.startingLine ?? 2
|
|
24
|
+
const relaxQuotes = options.relax ?? false
|
|
25
|
+
csvLoader._columns = options.columns
|
|
26
|
+
csvLoader.pump = (csvLoader) => {
|
|
27
|
+
const parser = parse({ delimiter: ',', from_line: fromLine, relax_quotes: relaxQuotes })
|
|
28
|
+
const transformer = new Transform({
|
|
29
|
+
readableObjectMode: true,
|
|
30
|
+
writableObjectMode: true,
|
|
31
|
+
emitClose: true,
|
|
32
|
+
transform (chunk, _, callback) {
|
|
33
|
+
chunk['_columns'] = options.columns
|
|
34
|
+
chunk['_linecount'] = lineCount
|
|
35
|
+
lineCount += 1
|
|
36
|
+
|
|
37
|
+
// remove non-printable characters
|
|
38
|
+
options.columns.forEach((_column, index) => {
|
|
39
|
+
if (chunk[index]) {
|
|
40
|
+
// eslint-disable-next-line no-control-regex
|
|
41
|
+
chunk[index] = chunk[index].replace(/[\x00-\x1F\x7F-\x9F]/g, '')
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
callback(null, chunk)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
return csvLoader
|
|
49
|
+
.pipe(parser)
|
|
50
|
+
.pipe(transformer)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return csvLoader
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
CSVLoader
|
|
58
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Miscellanous pipeline items
|
|
2
|
+
|
|
3
|
+
## PostgresSQLTask
|
|
4
|
+
|
|
5
|
+
Executes an arbitrary sql task either as part of another transformation or as an event
|
|
6
|
+
|
|
7
|
+
### Example
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
const {
|
|
11
|
+
Connections,
|
|
12
|
+
Destinations,
|
|
13
|
+
Etl,
|
|
14
|
+
Loaders,
|
|
15
|
+
Misc,
|
|
16
|
+
Validators,
|
|
17
|
+
Transformers,
|
|
18
|
+
} = require("ffc-pay-etl-framework");
|
|
19
|
+
|
|
20
|
+
const etl = new Etl.Etl();
|
|
21
|
+
|
|
22
|
+
etl
|
|
23
|
+
.connections(
|
|
24
|
+
new Connections.PostgresSQLConnection({
|
|
25
|
+
connectionname: "MyConnection",
|
|
26
|
+
database: "MyDB",
|
|
27
|
+
username: "MyUserName",
|
|
28
|
+
password: "MyPassword",
|
|
29
|
+
host: "localhost",
|
|
30
|
+
port: 5432,
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
.beforeETL(
|
|
34
|
+
new Misc.PostgresSQLTask({
|
|
35
|
+
connection: "MyConnection",
|
|
36
|
+
sql: "INSERT INTO MyTable (column1, column2) VALUES ('apples','oranges');",
|
|
37
|
+
})
|
|
38
|
+
)
|
|
39
|
+
.loader(new Loaders.CSVLoader({ path: csvFile, columns: columns }))
|
|
40
|
+
.destination(
|
|
41
|
+
new Destinations.PostgresDestination({
|
|
42
|
+
connection: "MyConnection",
|
|
43
|
+
table: "target",
|
|
44
|
+
mapping: [
|
|
45
|
+
{
|
|
46
|
+
column: "Dist Code",
|
|
47
|
+
targetColumn: "dist_code",
|
|
48
|
+
targetType: "string",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
includeErrors: false,
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
.pump();
|
|
55
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
const { PassThrough } = require('stream')
|
|
3
|
+
const startPosOffset = 3
|
|
4
|
+
const endPosOffset = 1
|
|
5
|
+
|
|
6
|
+
function getPlaceHolders (sql) {
|
|
7
|
+
const columnNamePlaceholderRegex = /(?<=\$\{)(.*?)(?=})/g
|
|
8
|
+
const matches = Array.from(sql.matchAll(columnNamePlaceholderRegex))
|
|
9
|
+
return matches.map(match => ({
|
|
10
|
+
match: match[0],
|
|
11
|
+
value: match[0].split('.')[1],
|
|
12
|
+
collection: match[0].split('.')[0],
|
|
13
|
+
startPos: match.index - startPosOffset,
|
|
14
|
+
endPos: match.index + match[0].length - endPosOffset
|
|
15
|
+
}))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function doPlaceHolderValueInterpolations (chunk, sql, placeholders) {
|
|
19
|
+
placeholders.forEach(placeholder => {
|
|
20
|
+
sql = sql.replace(new RegExp(`\\$\{${placeholder.match}}`),
|
|
21
|
+
chunk[chunk[`_${placeholder.collection}`].indexOf(placeholder.value)])
|
|
22
|
+
})
|
|
23
|
+
return sql
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function PostgresSQLTask (options) {
|
|
27
|
+
const passthrough = new PassThrough({
|
|
28
|
+
readableObjectMode: true,
|
|
29
|
+
writableObjectMode: true,
|
|
30
|
+
decodeStrings: false,
|
|
31
|
+
construct (callback) {
|
|
32
|
+
this.sql = options.sql
|
|
33
|
+
this.connectionname = options.connectionname
|
|
34
|
+
callback()
|
|
35
|
+
},
|
|
36
|
+
transform (chunk, _, callback) {
|
|
37
|
+
const placeholders = getPlaceHolders(this.sql)
|
|
38
|
+
if (placeholders.length === 0) {
|
|
39
|
+
this.connection.db.query(this.sql)
|
|
40
|
+
} else {
|
|
41
|
+
const interpolatedSql = doPlaceHolderValueInterpolations(chunk, this.sql, placeholders)
|
|
42
|
+
// TODO add more interpolation mechanisms to specify return values
|
|
43
|
+
// e.g. 'myReturnVal = SELECT MAX ID FROM TABLE;'
|
|
44
|
+
// and write to etl.store.myReturnVal or
|
|
45
|
+
// e.g. 'chunk.myReturnVal = SELECT MAX ID FROM TABLE;'
|
|
46
|
+
// and write to the chunk in say chunk.store.myReturnVal
|
|
47
|
+
this.connection.db.query(interpolatedSql)
|
|
48
|
+
}
|
|
49
|
+
callback(null, chunk)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
// Should definately split this out into a mixin
|
|
53
|
+
Object.assign(passthrough, {
|
|
54
|
+
setConnection: function (connection) {
|
|
55
|
+
this.connection = connection
|
|
56
|
+
}.bind(passthrough),
|
|
57
|
+
getConnectionName: function () {
|
|
58
|
+
return this.connection.name
|
|
59
|
+
}.bind(passthrough),
|
|
60
|
+
setETL: function (etl) {
|
|
61
|
+
this.etl = etl
|
|
62
|
+
}.bind(passthrough)
|
|
63
|
+
})
|
|
64
|
+
return passthrough
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
PostgresSQLTask,
|
|
69
|
+
getPlaceHolders,
|
|
70
|
+
doPlaceHolderValueInterpolations
|
|
71
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Transformers
|
|
2
|
+
|
|
3
|
+
Transformers manipulate row data and either replace the existing values or add new columns to each row depending on the type of transformer and the configuration.
|
|
4
|
+
|
|
5
|
+
Currently supported transformers are :
|
|
6
|
+
|
|
7
|
+
- ToUpperCaseTransformer
|
|
8
|
+
- FakerTransformer
|
|
9
|
+
|
|
10
|
+
## FakerTransformer
|
|
11
|
+
|
|
12
|
+
### Options
|
|
13
|
+
|
|
14
|
+
| option | description |
|
|
15
|
+
| ------- | ------------------------------------------------------------ |
|
|
16
|
+
| columns | Array of column transformations |
|
|
17
|
+
| locale | See https://fakerjs.dev/guide/localization for valid locales |
|
|
18
|
+
|
|
19
|
+
### Usage
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
FakerTransformer({
|
|
23
|
+
columns: [
|
|
24
|
+
{
|
|
25
|
+
name: "column2",
|
|
26
|
+
faker: "company.name",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
faker is a string value that can be any of the currently supported [apis](https://fakerjs.dev/api/)
|
|
33
|
+
|
|
34
|
+
e.g.
|
|
35
|
+
|
|
36
|
+
- finance.accountName
|
|
37
|
+
- person.firstName
|
|
38
|
+
- randomizer.next
|
|
39
|
+
|
|
40
|
+
To create UK post code
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
FakerTransformer({
|
|
44
|
+
columns: [
|
|
45
|
+
{
|
|
46
|
+
name: "column2",
|
|
47
|
+
faker: "location.zipCode",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
locale: "en_GB",
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Example
|
|
55
|
+
|
|
56
|
+
examples/csv-faker.js
|
|
57
|
+
|
|
58
|
+
## ToUpperCaseTransformer
|
|
59
|
+
|
|
60
|
+
### Options
|
|
61
|
+
|
|
62
|
+
| option | description |
|
|
63
|
+
| ------ | ------------------------------- |
|
|
64
|
+
| column | source column to be transformed |
|
|
65
|
+
|
|
66
|
+
### Usage
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
ToUpperCaseTransformer({
|
|
70
|
+
column: "column2",
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Example
|
|
75
|
+
|
|
76
|
+
examples/csv-to-upper-case.js
|
|
77
|
+
|
|
78
|
+
## StringReplaceTransformer
|
|
79
|
+
|
|
80
|
+
### Options
|
|
81
|
+
|
|
82
|
+
| option | description |
|
|
83
|
+
| ------- | --------------------------------------- |
|
|
84
|
+
| column | source column to be transformed |
|
|
85
|
+
| find | search string to be replaced |
|
|
86
|
+
| replace | value to replace the search string with |
|
|
87
|
+
|
|
88
|
+
### Usage
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
StringReplaceTransformer([
|
|
92
|
+
{
|
|
93
|
+
column: "column2",
|
|
94
|
+
find: "'",
|
|
95
|
+
replace: "''",
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Example
|
|
101
|
+
|
|
102
|
+
examples/csv-find-replace.js
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const { Transform } = require('node:stream')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} options
|
|
6
|
+
* @param {Array} options.columns {
|
|
7
|
+
* columns: [
|
|
8
|
+
* {
|
|
9
|
+
* name: "column-name",
|
|
10
|
+
* faker: "company.name"
|
|
11
|
+
* }
|
|
12
|
+
* ]
|
|
13
|
+
* }
|
|
14
|
+
* @param {String} options.locale
|
|
15
|
+
* @returns StreamReader
|
|
16
|
+
*/
|
|
17
|
+
function FakerTransformer (options) {
|
|
18
|
+
const self = this
|
|
19
|
+
self.columns = options.columns
|
|
20
|
+
let faker
|
|
21
|
+
if (options.locale) {
|
|
22
|
+
faker = require(`@faker-js/faker/locale/${options.locale}`).faker
|
|
23
|
+
} else {
|
|
24
|
+
faker = require('@faker-js/faker').faker
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getFaker (fakerType) {
|
|
28
|
+
return fakerType.split('.').reduce((a, b) => {
|
|
29
|
+
return Object.keys(a).length === 0 ? faker[b] : a[b]
|
|
30
|
+
}, {})
|
|
31
|
+
}
|
|
32
|
+
return new Transform({
|
|
33
|
+
readableObjectMode: true,
|
|
34
|
+
writableObjectMode: true,
|
|
35
|
+
transform (chunk, _, callback) {
|
|
36
|
+
const { _columns } = chunk
|
|
37
|
+
self.columns.forEach(column => {
|
|
38
|
+
const colIndex = _columns.indexOf(column.name)
|
|
39
|
+
chunk[colIndex] = getFaker(column.faker)()
|
|
40
|
+
})
|
|
41
|
+
callback(null, chunk)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
FakerTransformer
|
|
48
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const { ToUpperCaseTransformer } = require('./toUpperCaseTransformer')
|
|
2
|
+
const { FakerTransformer } = require('./fakerTransformer')
|
|
3
|
+
const { StringReplaceTransformer } = require('./stringReplaceTransformer')
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
StringReplaceTransformer,
|
|
7
|
+
ToUpperCaseTransformer,
|
|
8
|
+
FakerTransformer
|
|
9
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { Transform } = require('node:stream')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} options
|
|
6
|
+
* @param {String} options.column
|
|
7
|
+
* @returns Writable
|
|
8
|
+
*/
|
|
9
|
+
function StringReplaceTransformer (options) {
|
|
10
|
+
const self = this
|
|
11
|
+
self.replacements = options
|
|
12
|
+
|
|
13
|
+
return new Transform({
|
|
14
|
+
readableObjectMode: true,
|
|
15
|
+
writableObjectMode: true,
|
|
16
|
+
transform (chunk, _, callback) {
|
|
17
|
+
const { _columns } = chunk
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
self.replacements.forEach(r => {
|
|
20
|
+
const colIndex = _columns.indexOf(r.column)
|
|
21
|
+
if (r.all) {
|
|
22
|
+
chunk[colIndex] = chunk[colIndex].replaceAll(r.find, r.replace)
|
|
23
|
+
} else {
|
|
24
|
+
chunk[colIndex] = chunk[colIndex].replace(r.find, r.replace)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
callback(null, chunk)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
StringReplaceTransformer
|
|
35
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { Transform } = require('node:stream')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} options
|
|
6
|
+
* @param {String} options.column
|
|
7
|
+
* @returns Writable
|
|
8
|
+
*/
|
|
9
|
+
function ToUpperCaseTransformer (options) {
|
|
10
|
+
const self = this
|
|
11
|
+
self.column = options.column
|
|
12
|
+
|
|
13
|
+
return new Transform({
|
|
14
|
+
readableObjectMode: true,
|
|
15
|
+
writableObjectMode: true,
|
|
16
|
+
transform (chunk, _, callback) {
|
|
17
|
+
const { _columns } = chunk
|
|
18
|
+
const colIndex = _columns.indexOf(self.column)
|
|
19
|
+
chunk[colIndex] = chunk[colIndex].toUpperCase()
|
|
20
|
+
callback(null, chunk)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
ToUpperCaseTransformer
|
|
27
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Validators
|
|
2
|
+
|
|
3
|
+
Validators are expected to check specific columns within each row against specific validation rules
|
|
4
|
+
|
|
5
|
+
Currently supported destinations are :
|
|
6
|
+
|
|
7
|
+
- RequiredValidator
|
|
8
|
+
- UniqueValidator
|
|
9
|
+
- MultiToolValidator
|
|
10
|
+
|
|
11
|
+
## RequiredValidator
|
|
12
|
+
|
|
13
|
+
### Options
|
|
14
|
+
|
|
15
|
+
| option | description |
|
|
16
|
+
| -------------- | ------------------------------------------------------------- |
|
|
17
|
+
| columns | Array of column indexes |
|
|
18
|
+
|
|
19
|
+
### Usage
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
Validators.RequiredValidator({
|
|
23
|
+
columns: [0]
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Example
|
|
28
|
+
|
|
29
|
+
examples/csv-required-validator.js
|
|
30
|
+
|
|
31
|
+
## UniqueValidator
|
|
32
|
+
|
|
33
|
+
### Options
|
|
34
|
+
|
|
35
|
+
| option | description |
|
|
36
|
+
| -------------- | ------------------------------------------------------------- |
|
|
37
|
+
| column | Column to be tested for uniqueness |
|
|
38
|
+
|
|
39
|
+
### Usage
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
Validators.UniqueValidator(
|
|
43
|
+
{ column: "field_one" }
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Example
|
|
48
|
+
|
|
49
|
+
examples/csv-unique-validator.js
|
|
50
|
+
|
|
51
|
+
## MultiToolValidator
|
|
52
|
+
|
|
53
|
+
### Options
|
|
54
|
+
|
|
55
|
+
| option | description |
|
|
56
|
+
| -------------- | ------------------------------------------------------------- |
|
|
57
|
+
| column | Column to be tested for validity and validator specification |
|
|
58
|
+
|
|
59
|
+
NB Any validators specified [here](https://github.com/validatorjs/validator.js) can be used
|
|
60
|
+
|
|
61
|
+
### Usage
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
Validators.MultiToolValidator( { columns: [
|
|
65
|
+
{
|
|
66
|
+
name: "SOIL TYPE PERCENT (Percent)",
|
|
67
|
+
validator: "isAlphanumeric",
|
|
68
|
+
args: {
|
|
69
|
+
locale: "en-GB",
|
|
70
|
+
options: {
|
|
71
|
+
ignore: "-;%/ "
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Example
|
|
80
|
+
|
|
81
|
+
examples/csv-multi-tool-validator.js
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const { RequiredValidator } = require('./requiredValidator')
|
|
2
|
+
const { UniqueValidator } = require('./uniqueValidator')
|
|
3
|
+
const { MultiToolValidator } = require('./multiToolValidator')
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
RequiredValidator,
|
|
7
|
+
UniqueValidator,
|
|
8
|
+
MultiToolValidator
|
|
9
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
const { Transform } = require('stream')
|
|
3
|
+
const validator = require('validator')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} options
|
|
8
|
+
* @param {Array} options.columns { columns: [
|
|
9
|
+
* {
|
|
10
|
+
* name: "column-name",
|
|
11
|
+
* validator: "isAlphanumeric",
|
|
12
|
+
* args: {
|
|
13
|
+
* locale: "en-GB",
|
|
14
|
+
* options: {
|
|
15
|
+
* ignore: "-;%/ "
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* @returns Writable
|
|
22
|
+
*/
|
|
23
|
+
function MultiToolValidator (options) {
|
|
24
|
+
function getValidator (validatorType) {
|
|
25
|
+
return validator[validatorType]
|
|
26
|
+
}
|
|
27
|
+
return new Transform({
|
|
28
|
+
readableObjectMode: true,
|
|
29
|
+
writableObjectMode: true,
|
|
30
|
+
decodeStrings: false,
|
|
31
|
+
construct (callback) {
|
|
32
|
+
this.columns = options.columns
|
|
33
|
+
callback()
|
|
34
|
+
},
|
|
35
|
+
transform (chunk, _, callback) {
|
|
36
|
+
const { _columns } = chunk
|
|
37
|
+
this.columns.forEach(column => {
|
|
38
|
+
const colIndex = _columns.indexOf(column.name)
|
|
39
|
+
const vtr = getValidator(column.validator)
|
|
40
|
+
if (!vtr(chunk[colIndex], ...Object.values(column.args))) { chunk.errors.push(`${column.validator} returned invalid status in column ${colIndex}`) }
|
|
41
|
+
})
|
|
42
|
+
callback(null, chunk)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
MultiToolValidator
|
|
49
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
const { Transform } = require('stream')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} options
|
|
7
|
+
* @param {String} options.message
|
|
8
|
+
* @param {Array} options.columns
|
|
9
|
+
* @returns Transform
|
|
10
|
+
*/
|
|
11
|
+
function RequiredValidator (options) {
|
|
12
|
+
return new Transform({
|
|
13
|
+
readableObjectMode: true,
|
|
14
|
+
writableObjectMode: true,
|
|
15
|
+
decodeStrings: false,
|
|
16
|
+
construct (callback) {
|
|
17
|
+
this.message = options.message ? options.message : 'Required value is null'
|
|
18
|
+
this.requiredColumns = options.columns
|
|
19
|
+
callback()
|
|
20
|
+
},
|
|
21
|
+
transform (chunk, _, callback) {
|
|
22
|
+
this.requiredColumns.forEach(column => {
|
|
23
|
+
const requiredVal = chunk[column]
|
|
24
|
+
if (requiredVal === undefined || requiredVal === '') {
|
|
25
|
+
chunk.errors.push(`${this.message} in column ${column}`)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
callback(null, chunk)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
RequiredValidator
|
|
35
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const { Transform } = require('node:stream')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} options
|
|
6
|
+
* @param {String} options.message
|
|
7
|
+
* @param {String} options.column
|
|
8
|
+
* @returns Transform
|
|
9
|
+
*/
|
|
10
|
+
function UniqueValidator (options) {
|
|
11
|
+
const self = this
|
|
12
|
+
self.column = options.column
|
|
13
|
+
self.message = options.message ? options.message : 'Expected unique value is not unique'
|
|
14
|
+
self.uniqueColumnRows = []
|
|
15
|
+
function checkIsUnique (data) {
|
|
16
|
+
return self.uniqueColumnRows.indexOf(data) === -1
|
|
17
|
+
}
|
|
18
|
+
return new Transform({
|
|
19
|
+
readableObjectMode: true,
|
|
20
|
+
writableObjectMode: true,
|
|
21
|
+
decodeStrings: false,
|
|
22
|
+
construct (callback) {
|
|
23
|
+
callback()
|
|
24
|
+
},
|
|
25
|
+
transform (chunk, _, callback) {
|
|
26
|
+
const { _columns } = chunk
|
|
27
|
+
const colIndex = _columns.indexOf(self.column)
|
|
28
|
+
const uniqueValue = chunk[colIndex]
|
|
29
|
+
if (checkIsUnique(uniqueValue)) {
|
|
30
|
+
self.uniqueColumnRows.push(uniqueValue)
|
|
31
|
+
} else {
|
|
32
|
+
chunk.errors.push(`${self.message} ${self.column}`)
|
|
33
|
+
}
|
|
34
|
+
callback(null, chunk)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
UniqueValidator
|
|
41
|
+
}
|