csv-to-pg 0.6.2 → 2.0.2
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/LICENSE +1 -1
- package/cli.d.ts +2 -0
- package/cli.js +60 -0
- package/esm/cli.js +58 -0
- package/esm/index.js +3 -0
- package/esm/parse.js +257 -0
- package/esm/parser.js +59 -0
- package/esm/utils.js +267 -0
- package/index.d.ts +2 -0
- package/index.js +19 -0
- package/package.json +25 -55
- package/parse.d.ts +3 -0
- package/parse.js +289 -0
- package/parser.d.ts +4 -0
- package/parser.js +63 -0
- package/utils.d.ts +135 -0
- package/utils.js +300 -0
- package/main/cli.js +0 -96
- package/main/index.js +0 -29
- package/main/parse.js +0 -352
- package/main/parser.js +0 -139
- package/main/utils.js +0 -413
- package/module/cli.js +0 -59
- package/module/index.js +0 -2
- package/module/parse.js +0 -308
- package/module/parser.js +0 -67
- package/module/utils.js +0 -334
package/esm/utils.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import * as ast from 'pg-ast';
|
|
3
|
+
import { join, resolve } from 'path';
|
|
4
|
+
export const normalizePath = (path, cwd) => path.startsWith('/') ? path : resolve(join(cwd ? cwd : process.cwd(), path));
|
|
5
|
+
const lstr = (bbox) => {
|
|
6
|
+
const [lng1, lat1, lng2, lat2] = bbox.split(',').map((a) => a.trim());
|
|
7
|
+
return `LINESTRING(${lng1} ${lat1}, ${lng1} ${lat2}, ${lng2} ${lat2}, ${lng2} ${lat1}, ${lng1} ${lat1})`;
|
|
8
|
+
};
|
|
9
|
+
const funcCall = (name, args) => {
|
|
10
|
+
return ast.FuncCall({
|
|
11
|
+
funcname: [ast.String({ str: name })],
|
|
12
|
+
args
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const aflt = (num) => ast.A_Const({ val: ast.Float({ str: num }) });
|
|
16
|
+
const aint = (num) => ast.A_Const({ val: ast.Integer({ ival: num }) });
|
|
17
|
+
export const makeLocation = (longitude, latitude) => {
|
|
18
|
+
if (!longitude || !latitude) {
|
|
19
|
+
return ast.Null();
|
|
20
|
+
}
|
|
21
|
+
return funcCall('st_setsrid', [
|
|
22
|
+
funcCall('st_makepoint', [aflt(longitude), aflt(latitude)]),
|
|
23
|
+
aint(4326)
|
|
24
|
+
]);
|
|
25
|
+
};
|
|
26
|
+
// a string in the form of lon,lat,lon,lat
|
|
27
|
+
// -118.587533,34.024999,-118.495177,34.13165
|
|
28
|
+
export const makeBoundingBox = (bbox) => {
|
|
29
|
+
return funcCall('st_setsrid', [
|
|
30
|
+
funcCall('st_makepolygon', [
|
|
31
|
+
funcCall('st_geomfromtext', [
|
|
32
|
+
ast.A_Const({ val: ast.String({ str: lstr(bbox) }) })
|
|
33
|
+
])
|
|
34
|
+
]),
|
|
35
|
+
aint(4326)
|
|
36
|
+
]);
|
|
37
|
+
};
|
|
38
|
+
const ValuesLists = ({ types, record }) => Object.entries(types).map(([field, type]) => {
|
|
39
|
+
if (typeof type === 'function') {
|
|
40
|
+
return type(record);
|
|
41
|
+
}
|
|
42
|
+
throw new Error('coercion function missing');
|
|
43
|
+
});
|
|
44
|
+
const makeCast = (arg, type) => ({
|
|
45
|
+
TypeCast: {
|
|
46
|
+
arg,
|
|
47
|
+
typeName: {
|
|
48
|
+
TypeName: {
|
|
49
|
+
names: [
|
|
50
|
+
{
|
|
51
|
+
String: {
|
|
52
|
+
str: type
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
typemod: -1
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const ref = (name) => ({
|
|
62
|
+
ResTarget: {
|
|
63
|
+
name,
|
|
64
|
+
val: {
|
|
65
|
+
ColumnRef: {
|
|
66
|
+
fields: [
|
|
67
|
+
{
|
|
68
|
+
String: {
|
|
69
|
+
str: 'excluded'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
String: {
|
|
74
|
+
str: name
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const indexElem = (name) => ({
|
|
83
|
+
IndexElem: {
|
|
84
|
+
name,
|
|
85
|
+
ordering: 0,
|
|
86
|
+
nulls_ordering: 0
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const makeConflictClause = (conflictElems, fields) => {
|
|
90
|
+
if (!conflictElems || !conflictElems.length)
|
|
91
|
+
return undefined;
|
|
92
|
+
const setElems = fields.filter((el) => !conflictElems.includes(el));
|
|
93
|
+
if (setElems.length) {
|
|
94
|
+
return {
|
|
95
|
+
OnConflictClause: {
|
|
96
|
+
action: 2,
|
|
97
|
+
infer: {
|
|
98
|
+
InferClause: {
|
|
99
|
+
indexElems: conflictElems.map((a) => indexElem(a))
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
targetList: setElems.map((a) => ref(a))
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return {
|
|
108
|
+
OnConflictClause: {
|
|
109
|
+
action: 1,
|
|
110
|
+
infer: {
|
|
111
|
+
InferClause: {
|
|
112
|
+
indexElems: conflictElems.map((a) => indexElem(a))
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
export const InsertOne = ({ schema = 'public', table, types, record, conflict }) => ({
|
|
120
|
+
RawStmt: {
|
|
121
|
+
stmt: {
|
|
122
|
+
InsertStmt: {
|
|
123
|
+
relation: {
|
|
124
|
+
RangeVar: {
|
|
125
|
+
schemaname: schema,
|
|
126
|
+
relname: table,
|
|
127
|
+
inh: true,
|
|
128
|
+
relpersistence: 'p'
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
cols: Object.keys(types).map((field) => ast.ResTarget({ name: field })),
|
|
132
|
+
selectStmt: {
|
|
133
|
+
SelectStmt: {
|
|
134
|
+
valuesLists: [ValuesLists({ types, record })],
|
|
135
|
+
op: 0
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
onConflictClause: makeConflictClause(conflict, Object.keys(types)),
|
|
139
|
+
override: 0
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
stmt_len: 1
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
export const InsertMany = ({ schema = 'public', table, types, records, conflict }) => ({
|
|
146
|
+
RawStmt: {
|
|
147
|
+
stmt: {
|
|
148
|
+
InsertStmt: {
|
|
149
|
+
relation: {
|
|
150
|
+
RangeVar: {
|
|
151
|
+
schemaname: schema,
|
|
152
|
+
relname: table,
|
|
153
|
+
inh: true,
|
|
154
|
+
relpersistence: 'p'
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
cols: Object.keys(types).map((field) => ast.ResTarget({ name: field })),
|
|
158
|
+
selectStmt: {
|
|
159
|
+
SelectStmt: {
|
|
160
|
+
valuesLists: records.map((record) => ValuesLists({ types, record })),
|
|
161
|
+
op: 0
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
onConflictClause: makeConflictClause(conflict, Object.keys(types)),
|
|
165
|
+
override: 0
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
stmt_len: 1
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
export const wrapValue = (val, { wrap, wrapAst, cast } = {}) => {
|
|
172
|
+
if (Array.isArray(wrap)) {
|
|
173
|
+
val = ast.FuncCall({
|
|
174
|
+
funcname: wrap.map((n) => ast.String({ str: n })),
|
|
175
|
+
args: [val]
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (wrapAst)
|
|
179
|
+
return wrapAst(val);
|
|
180
|
+
if (cast)
|
|
181
|
+
return makeCast(val, cast);
|
|
182
|
+
return val;
|
|
183
|
+
};
|
|
184
|
+
export const getRelatedField = ({ schema = 'public', table, refType, refKey, refField, wrap, wrapAst, cast, record, parse, from }) => {
|
|
185
|
+
let val;
|
|
186
|
+
const value = parse(record[from[0]]);
|
|
187
|
+
if (typeof value === 'undefined') {
|
|
188
|
+
return ast.Null({});
|
|
189
|
+
}
|
|
190
|
+
switch (refType) {
|
|
191
|
+
case 'int':
|
|
192
|
+
val = ast.A_Const({ val: ast.Integer({ ival: value }) });
|
|
193
|
+
break;
|
|
194
|
+
case 'float':
|
|
195
|
+
val = ast.A_Const({ val: ast.Float({ str: value }) });
|
|
196
|
+
break;
|
|
197
|
+
case 'boolean':
|
|
198
|
+
case 'bool':
|
|
199
|
+
val = ast.String({ str: value ? 'TRUE' : 'FALSE' });
|
|
200
|
+
break;
|
|
201
|
+
case 'text':
|
|
202
|
+
default:
|
|
203
|
+
val = ast.A_Const({ val: ast.String({ str: value }) });
|
|
204
|
+
}
|
|
205
|
+
val = wrapValue(val, { wrap, wrapAst });
|
|
206
|
+
return wrapValue({
|
|
207
|
+
SubLink: {
|
|
208
|
+
subLinkType: 4,
|
|
209
|
+
subselect: {
|
|
210
|
+
SelectStmt: {
|
|
211
|
+
targetList: [
|
|
212
|
+
{
|
|
213
|
+
ResTarget: {
|
|
214
|
+
val: {
|
|
215
|
+
ColumnRef: {
|
|
216
|
+
fields: [
|
|
217
|
+
{
|
|
218
|
+
String: {
|
|
219
|
+
str: refKey
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
fromClause: [
|
|
229
|
+
{
|
|
230
|
+
RangeVar: {
|
|
231
|
+
schemaname: schema,
|
|
232
|
+
relname: table,
|
|
233
|
+
inh: true,
|
|
234
|
+
relpersistence: 'p'
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
],
|
|
238
|
+
whereClause: {
|
|
239
|
+
A_Expr: {
|
|
240
|
+
kind: 0,
|
|
241
|
+
name: [
|
|
242
|
+
{
|
|
243
|
+
String: {
|
|
244
|
+
str: '='
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
lexpr: {
|
|
249
|
+
ColumnRef: {
|
|
250
|
+
fields: [
|
|
251
|
+
{
|
|
252
|
+
String: {
|
|
253
|
+
str: refField
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
rexpr: val
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
op: 0
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}, { cast });
|
|
267
|
+
};
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// @ts-nocheck
|
|
18
|
+
__exportStar(require("./parse"), exports);
|
|
19
|
+
__exportStar(require("./parser"), exports);
|
package/package.json
CHANGED
|
@@ -1,74 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "csv-to-pg",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "csv to pg statements",
|
|
3
|
+
"version": "2.0.2",
|
|
5
4
|
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
6
|
-
"
|
|
5
|
+
"description": "csv to pg statements",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"module": "esm/index.js",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"homepage": "https://github.com/launchql/launchql",
|
|
7
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"lib": "src",
|
|
12
|
-
"test": "__tests__"
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"directory": "dist"
|
|
13
14
|
},
|
|
14
|
-
"files": [
|
|
15
|
-
"main",
|
|
16
|
-
"module"
|
|
17
|
-
],
|
|
18
15
|
"bin": {
|
|
19
|
-
"csv2pg": "
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build:main": "cross-env BABEL_ENV=production babel src --out-dir main --delete-dir-on-start",
|
|
23
|
-
"build:module": "cross-env MODULE=true babel src --out-dir module --delete-dir-on-start",
|
|
24
|
-
"build": "npm run build:module && npm run build:main",
|
|
25
|
-
"prepublish": "npm run build",
|
|
26
|
-
"dev": "cross-env NODE_ENV=development babel-node src/cli",
|
|
27
|
-
"watch": "cross-env NODE_ENV=development babel-watch src/index",
|
|
28
|
-
"lint": "eslint src --fix",
|
|
29
|
-
"test": "jest",
|
|
30
|
-
"test:watch": "jest --watch",
|
|
31
|
-
"test:debug": "node --inspect node_modules/.bin/jest --runInBand"
|
|
32
|
-
},
|
|
33
|
-
"publishConfig": {
|
|
34
|
-
"access": "public"
|
|
16
|
+
"csv2pg": "cli.js"
|
|
35
17
|
},
|
|
36
18
|
"repository": {
|
|
37
19
|
"type": "git",
|
|
38
|
-
"url": "https://github.com/
|
|
20
|
+
"url": "https://github.com/launchql/launchql"
|
|
39
21
|
},
|
|
40
|
-
"keywords": [],
|
|
41
22
|
"bugs": {
|
|
42
|
-
"url": "https://github.com/
|
|
23
|
+
"url": "https://github.com/launchql/launchql/issues"
|
|
43
24
|
},
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"babel-core": "7.0.0-bridge.0",
|
|
54
|
-
"babel-eslint": "10.1.0",
|
|
55
|
-
"babel-jest": "25.1.0",
|
|
56
|
-
"babel-watch": "^7.0.0",
|
|
57
|
-
"cross-env": "^7.0.2",
|
|
58
|
-
"eslint": "6.8.0",
|
|
59
|
-
"eslint-config-prettier": "^6.10.0",
|
|
60
|
-
"eslint-plugin-prettier": "^3.1.2",
|
|
61
|
-
"jest": "^24.5.0",
|
|
62
|
-
"jest-in-case": "^1.0.2",
|
|
63
|
-
"prettier": "^2.1.2",
|
|
64
|
-
"regenerator-runtime": "^0.13.7"
|
|
25
|
+
"scripts": {
|
|
26
|
+
"copy": "copyfiles -f ../../LICENSE README.md package.json dist",
|
|
27
|
+
"clean": "rimraf dist/**",
|
|
28
|
+
"prepare": "npm run build",
|
|
29
|
+
"build": "npm run clean; tsc; tsc -p tsconfig.esm.json; npm run copy",
|
|
30
|
+
"build:dev": "npm run clean; tsc --declarationMap; tsc -p tsconfig.esm.json; npm run copy",
|
|
31
|
+
"lint": "eslint . --fix",
|
|
32
|
+
"test": "jest",
|
|
33
|
+
"test:watch": "jest --watch"
|
|
65
34
|
},
|
|
35
|
+
"keywords": [],
|
|
66
36
|
"dependencies": {
|
|
67
|
-
"@babel/runtime": "^7.11.2",
|
|
68
37
|
"@pyramation/prompt": "^0.0.1",
|
|
69
38
|
"csv-parser": "^2.3.3",
|
|
70
39
|
"js-yaml": "^3.14.0",
|
|
71
|
-
"pg-ast": "^0.
|
|
40
|
+
"pg-ast": "^2.0.2",
|
|
72
41
|
"pgsql-deparser": "^1.2.3"
|
|
73
|
-
}
|
|
42
|
+
},
|
|
43
|
+
"gitHead": "d4ef195aec8d4c5a470f507b5a57c6c8e81124f4"
|
|
74
44
|
}
|
package/parse.d.ts
ADDED
package/parse.js
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.parseTypes = exports.readConfig = exports.parse = void 0;
|
|
31
|
+
const csv_parser_1 = __importDefault(require("csv-parser"));
|
|
32
|
+
const fs_1 = require("fs");
|
|
33
|
+
const js_yaml_1 = require("js-yaml");
|
|
34
|
+
const ast = __importStar(require("pg-ast"));
|
|
35
|
+
const utils_1 = require("./utils");
|
|
36
|
+
function isNumeric(str) {
|
|
37
|
+
if (typeof str === 'number')
|
|
38
|
+
return true;
|
|
39
|
+
if (typeof str !== 'string')
|
|
40
|
+
return false; // we only process strings!
|
|
41
|
+
return (!isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
|
|
42
|
+
!isNaN(parseFloat(str))); // ...and ensure strings of whitespace fail
|
|
43
|
+
}
|
|
44
|
+
const parseJson = (value) => {
|
|
45
|
+
if (typeof value === 'string')
|
|
46
|
+
return value;
|
|
47
|
+
return value && JSON.stringify(value);
|
|
48
|
+
};
|
|
49
|
+
const psqlArray = (value) => {
|
|
50
|
+
if (value && value.length) {
|
|
51
|
+
return `{${value.map((v) => v)}}`;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const parse = (path, opts) => new Promise((resolve, reject) => {
|
|
55
|
+
const results = [];
|
|
56
|
+
(0, fs_1.createReadStream)(path)
|
|
57
|
+
.pipe((0, csv_parser_1.default)(opts))
|
|
58
|
+
// TODO check if 'data' is guaranteed to have a full row,
|
|
59
|
+
// if so, make a hook to use the stream properly
|
|
60
|
+
.on('data', (data) => results.push(data))
|
|
61
|
+
.on('error', (er) => {
|
|
62
|
+
reject(er);
|
|
63
|
+
})
|
|
64
|
+
.on('end', () => {
|
|
65
|
+
resolve(results);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
exports.parse = parse;
|
|
69
|
+
const readConfig = (config) => {
|
|
70
|
+
let configValue;
|
|
71
|
+
if (config.endsWith('.js')) {
|
|
72
|
+
configValue = require(config);
|
|
73
|
+
}
|
|
74
|
+
else if (config.endsWith('json')) {
|
|
75
|
+
configValue = JSON.parse((0, fs_1.readFileSync)(config, 'utf-8'));
|
|
76
|
+
}
|
|
77
|
+
else if (config.endsWith('yaml') || config.endsWith('yml')) {
|
|
78
|
+
configValue = (0, js_yaml_1.safeLoad)((0, fs_1.readFileSync)(config, 'utf-8'));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
throw new Error('unsupported config!');
|
|
82
|
+
}
|
|
83
|
+
return configValue;
|
|
84
|
+
};
|
|
85
|
+
exports.readConfig = readConfig;
|
|
86
|
+
const getFromValue = (from) => {
|
|
87
|
+
if (Array.isArray(from))
|
|
88
|
+
return from;
|
|
89
|
+
return [from];
|
|
90
|
+
};
|
|
91
|
+
// looks like the CSV library gives us empty strings?
|
|
92
|
+
const cleanseEmptyStrings = (str) => {
|
|
93
|
+
if (typeof str === 'string') {
|
|
94
|
+
if (str.trim() === '')
|
|
95
|
+
return null;
|
|
96
|
+
return str;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return str;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const parseBoolean = (str) => {
|
|
103
|
+
if (typeof str === 'boolean') {
|
|
104
|
+
return str;
|
|
105
|
+
}
|
|
106
|
+
else if (typeof str === 'string') {
|
|
107
|
+
const s = str.toLowerCase();
|
|
108
|
+
if (s === 'true') {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
else if (s === 't') {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
else if (s === 'f') {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
else if (s === 'false') {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const getValuesFromKeys = (object, keys) => keys.map((key) => object[key]);
|
|
127
|
+
const identity = (a) => a;
|
|
128
|
+
const isEmpty = (value) => value === null || typeof value === 'undefined';
|
|
129
|
+
// type (int, text, etc)
|
|
130
|
+
// from Array of keys that map to records found (e.g., ['lon', 'lat'])
|
|
131
|
+
const getCoercionFunc = (type, from, opts) => {
|
|
132
|
+
const parse = (opts.parse = opts.parse || identity);
|
|
133
|
+
switch (type) {
|
|
134
|
+
case 'int':
|
|
135
|
+
return (record) => {
|
|
136
|
+
const value = parse(record[from[0]]);
|
|
137
|
+
if (isEmpty(value)) {
|
|
138
|
+
return ast.Null({});
|
|
139
|
+
}
|
|
140
|
+
if (!isNumeric(value)) {
|
|
141
|
+
return ast.Null({});
|
|
142
|
+
}
|
|
143
|
+
const val = ast.A_Const({
|
|
144
|
+
val: ast.Integer({ ival: value })
|
|
145
|
+
});
|
|
146
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
147
|
+
};
|
|
148
|
+
case 'float':
|
|
149
|
+
return (record) => {
|
|
150
|
+
const value = parse(record[from[0]]);
|
|
151
|
+
if (isEmpty(value)) {
|
|
152
|
+
return ast.Null({});
|
|
153
|
+
}
|
|
154
|
+
if (!isNumeric(value)) {
|
|
155
|
+
return ast.Null({});
|
|
156
|
+
}
|
|
157
|
+
const val = ast.A_Const({
|
|
158
|
+
val: ast.Float({ str: value })
|
|
159
|
+
});
|
|
160
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
161
|
+
};
|
|
162
|
+
case 'boolean':
|
|
163
|
+
case 'bool':
|
|
164
|
+
return (record) => {
|
|
165
|
+
const value = parse(parseBoolean(record[from[0]]));
|
|
166
|
+
if (isEmpty(value)) {
|
|
167
|
+
return ast.Null({});
|
|
168
|
+
}
|
|
169
|
+
const val = ast.String({
|
|
170
|
+
str: value ? 'TRUE' : 'FALSE'
|
|
171
|
+
});
|
|
172
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
173
|
+
};
|
|
174
|
+
case 'bbox':
|
|
175
|
+
// do bbox magic with args from the fields
|
|
176
|
+
return (record) => {
|
|
177
|
+
const val = (0, utils_1.makeBoundingBox)(parse(record[from[0]]));
|
|
178
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
179
|
+
};
|
|
180
|
+
case 'location':
|
|
181
|
+
return (record) => {
|
|
182
|
+
const [lon, lat] = getValuesFromKeys(record, from);
|
|
183
|
+
if (typeof lon === 'undefined') {
|
|
184
|
+
return ast.Null({});
|
|
185
|
+
}
|
|
186
|
+
if (typeof lat === 'undefined') {
|
|
187
|
+
return ast.Null({});
|
|
188
|
+
}
|
|
189
|
+
if (!isNumeric(lon) || !isNumeric(lat)) {
|
|
190
|
+
return ast.Null({});
|
|
191
|
+
}
|
|
192
|
+
// NO parse here...
|
|
193
|
+
const val = (0, utils_1.makeLocation)(lon, lat);
|
|
194
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
195
|
+
};
|
|
196
|
+
case 'related':
|
|
197
|
+
return (record) => {
|
|
198
|
+
return (0, utils_1.getRelatedField)({
|
|
199
|
+
...opts,
|
|
200
|
+
record,
|
|
201
|
+
from
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
case 'uuid':
|
|
205
|
+
return (record) => {
|
|
206
|
+
const value = parse(record[from[0]]);
|
|
207
|
+
if (isEmpty(value) ||
|
|
208
|
+
!/^([0-9a-fA-F]{8})-(([0-9a-fA-F]{4}-){3})([0-9a-fA-F]{12})$/i.test(value)) {
|
|
209
|
+
return ast.Null({});
|
|
210
|
+
}
|
|
211
|
+
const val = ast.A_Const({
|
|
212
|
+
val: ast.String({ str: value })
|
|
213
|
+
});
|
|
214
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
215
|
+
};
|
|
216
|
+
case 'text':
|
|
217
|
+
return (record) => {
|
|
218
|
+
const value = parse(cleanseEmptyStrings(record[from[0]]));
|
|
219
|
+
if (isEmpty(value)) {
|
|
220
|
+
return ast.Null({});
|
|
221
|
+
}
|
|
222
|
+
const val = ast.A_Const({
|
|
223
|
+
val: ast.String({ str: value })
|
|
224
|
+
});
|
|
225
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
226
|
+
};
|
|
227
|
+
case 'text[]':
|
|
228
|
+
return (record) => {
|
|
229
|
+
const value = parse(psqlArray(cleanseEmptyStrings(record[from[0]])));
|
|
230
|
+
if (isEmpty(value)) {
|
|
231
|
+
return ast.Null({});
|
|
232
|
+
}
|
|
233
|
+
const val = ast.A_Const({
|
|
234
|
+
val: ast.String({ str: value })
|
|
235
|
+
});
|
|
236
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
237
|
+
};
|
|
238
|
+
case 'image':
|
|
239
|
+
case 'attachment':
|
|
240
|
+
case 'json':
|
|
241
|
+
case 'jsonb':
|
|
242
|
+
return (record) => {
|
|
243
|
+
const value = parse(parseJson(cleanseEmptyStrings(record[from[0]])));
|
|
244
|
+
if (isEmpty(value)) {
|
|
245
|
+
return ast.Null({});
|
|
246
|
+
}
|
|
247
|
+
const val = ast.A_Const({
|
|
248
|
+
val: ast.String({ str: value })
|
|
249
|
+
});
|
|
250
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
251
|
+
};
|
|
252
|
+
default:
|
|
253
|
+
return (record) => {
|
|
254
|
+
const value = parse(cleanseEmptyStrings(record[from[0]]));
|
|
255
|
+
if (isEmpty(value)) {
|
|
256
|
+
return ast.Null({});
|
|
257
|
+
}
|
|
258
|
+
const val = ast.A_Const({
|
|
259
|
+
val: ast.String({ str: value })
|
|
260
|
+
});
|
|
261
|
+
return (0, utils_1.wrapValue)(val, opts);
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
const parseTypes = (config) => {
|
|
266
|
+
return Object.entries(config.fields).reduce((m, v) => {
|
|
267
|
+
let [key, value] = v;
|
|
268
|
+
let type;
|
|
269
|
+
let from;
|
|
270
|
+
if (typeof value === 'string') {
|
|
271
|
+
type = value;
|
|
272
|
+
from = [key];
|
|
273
|
+
if (['related', 'location'].includes(type)) {
|
|
274
|
+
throw new Error('must use object for ' + type + ' type');
|
|
275
|
+
}
|
|
276
|
+
value = {
|
|
277
|
+
type,
|
|
278
|
+
from
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
type = value.type;
|
|
283
|
+
from = getFromValue(value.from || key);
|
|
284
|
+
}
|
|
285
|
+
m[key] = getCoercionFunc(type, from, value);
|
|
286
|
+
return m;
|
|
287
|
+
}, {});
|
|
288
|
+
};
|
|
289
|
+
exports.parseTypes = parseTypes;
|
package/parser.d.ts
ADDED