@budibase/server 0.9.184 → 0.9.185-alpha.11
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/Dockerfile +1 -1
- package/__mocks__/mssql.ts +1 -1
- package/__mocks__/pg.ts +3 -0
- package/builder/assets/bb-space-man.5be51a45.svg +290 -0
- package/builder/assets/index.053c6f7a.js +1321 -0
- package/builder/assets/index.4a7c5636.css +1 -0
- package/builder/assets/vendor.470042a4.js +152 -0
- package/builder/assets/vendor.5e1fe644.css +1 -0
- package/builder/index.html +4 -4
- package/coverage/clover.xml +1357 -959
- package/coverage/coverage-final.json +91 -87
- package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
- package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
- package/coverage/lcov-report/api/controllers/application.js.html +65 -14
- package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
- package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +4 -4
- package/coverage/lcov-report/api/controllers/datasource.js.html +147 -27
- package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
- package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
- package/coverage/lcov-report/api/controllers/index.html +90 -90
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +3 -3
- package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
- package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
- package/coverage/lcov-report/api/controllers/query.js.html +53 -209
- package/coverage/lcov-report/api/controllers/role.js.html +9 -9
- package/coverage/lcov-report/api/controllers/routing.js.html +3 -3
- package/coverage/lcov-report/api/controllers/row/external.js.html +12 -6
- package/coverage/lcov-report/api/controllers/row/index.html +26 -26
- package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
- package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
- package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
- package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
- package/coverage/lcov-report/api/controllers/script.js.html +3 -3
- package/coverage/lcov-report/api/controllers/table/external.js.html +980 -0
- package/coverage/lcov-report/api/controllers/table/index.html +56 -26
- package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
- package/coverage/lcov-report/api/controllers/table/internal.js.html +545 -0
- package/coverage/lcov-report/api/controllers/table/utils.js.html +172 -49
- package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
- package/coverage/lcov-report/api/controllers/user.js.html +390 -33
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +9 -9
- package/coverage/lcov-report/api/controllers/view/index.js.html +5 -5
- package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
- package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/application.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +2 -2
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +4 -4
- package/coverage/lcov-report/api/routes/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +5 -2
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/row.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/static.js.html +2 -2
- package/coverage/lcov-report/api/routes/table.js.html +35 -2
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +17 -2
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +2 -2
- package/coverage/lcov-report/automations/automationUtils.js.html +3 -3
- package/coverage/lcov-report/automations/bullboard.js.html +71 -20
- package/coverage/lcov-report/automations/index.html +33 -48
- package/coverage/lcov-report/automations/index.js.html +2 -2
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +14 -14
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
- package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
- package/coverage/lcov-report/automations/triggers.js.html +22 -10
- package/coverage/lcov-report/automations/utils.js.html +42 -12
- package/coverage/lcov-report/constants/index.html +5 -5
- package/coverage/lcov-report/constants/index.js.html +49 -4
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/client.js.html +2 -2
- package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
- package/coverage/lcov-report/db/index.html +2 -2
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
- package/coverage/lcov-report/db/linkedRows/index.html +10 -10
- package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
- package/coverage/lcov-report/db/newid.js.html +1 -1
- package/coverage/lcov-report/db/utils.js.html +1 -1
- package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
- package/coverage/lcov-report/events/index.html +6 -6
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +5 -5
- package/coverage/lcov-report/index.html +98 -83
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
- package/coverage/lcov-report/middleware/authorized.js.html +11 -8
- package/coverage/lcov-report/middleware/builder.js.html +16 -7
- package/coverage/lcov-report/middleware/currentapp.js.html +6 -6
- package/coverage/lcov-report/middleware/index.html +23 -23
- package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
- package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
- package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
- package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
- package/coverage/lcov-report/middleware/utils.js.html +1 -1
- package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +83 -32
- package/coverage/lcov-report/threads/index.html +141 -0
- package/coverage/lcov-report/threads/index.js.html +260 -0
- package/coverage/lcov-report/threads/query.js.html +269 -0
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
- package/coverage/lcov-report/utilities/global.js.html +26 -11
- package/coverage/lcov-report/utilities/index.html +49 -49
- package/coverage/lcov-report/utilities/index.js.html +65 -17
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +30 -9
- package/coverage/lcov-report/utilities/queue/index.html +14 -14
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
- package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
- package/coverage/lcov-report/utilities/security.js.html +3 -3
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
- package/coverage/lcov.info +2301 -1599
- package/dist/api/controllers/application.js +16 -2
- package/dist/api/controllers/auth.js +3 -3
- package/dist/api/controllers/datasource.js +47 -16
- package/dist/api/controllers/deploy/index.js +2 -0
- package/dist/api/controllers/dev.js +4 -1
- package/dist/api/controllers/query.js +30 -58
- package/dist/api/controllers/row/ExternalRequest.js +37 -14
- package/dist/api/controllers/row/external.js +1 -0
- package/dist/api/controllers/row/utils.js +6 -12
- package/dist/api/controllers/static/index.js +1 -1
- package/dist/api/controllers/table/external.js +242 -0
- package/dist/api/controllers/table/index.js +49 -123
- package/dist/api/controllers/table/internal.js +150 -0
- package/dist/api/controllers/table/utils.js +62 -28
- package/dist/api/controllers/user.js +123 -11
- package/dist/api/routes/query.js +1 -1
- package/dist/api/routes/table.js +7 -1
- package/dist/api/routes/user.js +2 -1
- package/dist/app.js +2 -11
- package/dist/automations/bullboard.js +37 -10
- package/dist/automations/triggers.js +7 -3
- package/dist/automations/utils.js +11 -2
- package/dist/constants/index.js +13 -0
- package/dist/definitions/datasource.js +4 -0
- package/dist/integrations/base/IntegrationBase.js +2 -0
- package/dist/integrations/base/datasourcePlus.js +2 -0
- package/dist/integrations/base/sql.js +334 -180
- package/dist/integrations/base/sqlTable.js +143 -0
- package/dist/integrations/base/utils.js +30 -0
- package/dist/integrations/microsoftSqlServer.js +117 -9
- package/dist/integrations/mysql.js +14 -84
- package/dist/integrations/postgres.js +35 -28
- package/dist/integrations/utils.js +56 -9
- package/dist/middleware/authorized.js +2 -1
- package/dist/middleware/builder.js +4 -1
- package/dist/package.json +7 -6
- package/dist/tests/utilities/TestConfiguration.js +2 -1
- package/dist/{automations/thread.js → threads/automation.js} +23 -7
- package/dist/threads/index.js +58 -0
- package/dist/threads/query.js +61 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/csvParser.js +46 -5
- package/dist/utilities/global.js +6 -2
- package/dist/utilities/index.js +18 -2
- package/dist/utilities/queue/inMemoryQueue.js +8 -0
- package/dist/utilities/scriptRunner.js +4 -1
- package/package.json +8 -7
- package/scripts/integrations/mssql/data/Dockerfile +9 -0
- package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
- package/scripts/integrations/mssql/data/setup.sql +54 -0
- package/scripts/integrations/mssql/docker-compose.yaml +12 -0
- package/scripts/integrations/mssql/reset.sh +3 -0
- package/scripts/likeCypress.ts +33 -0
- package/src/api/controllers/application.js +19 -2
- package/src/api/controllers/auth.js +3 -0
- package/src/api/controllers/datasource.js +56 -16
- package/src/api/controllers/deploy/index.js +2 -0
- package/src/api/controllers/dev.js +4 -1
- package/src/api/controllers/query.js +29 -81
- package/src/api/controllers/row/ExternalRequest.ts +47 -16
- package/src/api/controllers/row/external.js +2 -0
- package/src/api/controllers/row/utils.js +8 -11
- package/src/api/controllers/static/index.js +1 -1
- package/src/api/controllers/table/external.js +300 -0
- package/src/api/controllers/table/index.js +47 -137
- package/src/api/controllers/table/internal.js +155 -0
- package/src/api/controllers/table/utils.js +74 -33
- package/src/api/controllers/user.js +132 -13
- package/src/api/routes/query.js +1 -0
- package/src/api/routes/table.js +11 -0
- package/src/api/routes/tests/datasource.spec.js +2 -2
- package/src/api/routes/tests/misc.spec.js +5 -1
- package/src/api/routes/tests/query.spec.js +0 -1
- package/src/api/routes/user.js +5 -0
- package/src/app.ts +2 -10
- package/src/automations/bullboard.js +28 -11
- package/src/automations/tests/automation.spec.js +3 -4
- package/src/automations/triggers.js +7 -3
- package/src/automations/utils.js +12 -2
- package/src/constants/index.js +15 -0
- package/src/definitions/common.ts +7 -1
- package/src/definitions/datasource.ts +9 -3
- package/src/integrations/airtable.ts +2 -1
- package/src/integrations/arangodb.ts +2 -1
- package/src/integrations/base/IntegrationBase.ts +6 -0
- package/src/integrations/base/datasourcePlus.ts +9 -0
- package/src/integrations/base/sql.ts +359 -222
- package/src/integrations/base/sqlTable.ts +180 -0
- package/src/integrations/base/utils.ts +22 -0
- package/src/integrations/couchdb.ts +2 -1
- package/src/integrations/dynamodb.ts +2 -1
- package/src/integrations/elasticsearch.ts +2 -1
- package/src/integrations/microsoftSqlServer.ts +145 -13
- package/src/integrations/mongodb.ts +2 -1
- package/src/integrations/mysql.ts +20 -94
- package/src/integrations/postgres.ts +45 -35
- package/src/integrations/rest.ts +2 -1
- package/src/integrations/s3.ts +2 -1
- package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
- package/src/integrations/tests/postgres.spec.js +4 -0
- package/src/integrations/utils.ts +60 -8
- package/src/middleware/authorized.js +2 -1
- package/src/middleware/builder.js +4 -1
- package/src/tests/utilities/TestConfiguration.js +2 -1
- package/src/{automations/thread.js → threads/automation.js} +24 -7
- package/src/threads/index.js +60 -0
- package/src/threads/query.js +63 -0
- package/src/utilities/csvParser.js +48 -5
- package/src/utilities/global.js +7 -2
- package/src/utilities/index.js +18 -2
- package/src/utilities/queue/inMemoryQueue.js +7 -0
- package/src/utilities/scriptRunner.js +4 -1
- package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
- package/src/utilities/tests/csvParser.spec.js +3 -0
- package/tsconfig.json +1 -1
- package/builder/assets/index.34964222.js +0 -201
- package/builder/assets/index.7469e1e4.css +0 -29
- package/builder/assets/vendor.278a305c.js +0 -9
- package/builder/assets/vendor.d75d5eb7.css +0 -1
|
@@ -6,17 +6,18 @@ import {
|
|
|
6
6
|
SqlQuery,
|
|
7
7
|
} from "../definitions/datasource"
|
|
8
8
|
import { Table } from "../definitions/common"
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getSqlQuery,
|
|
11
|
+
buildExternalTableId,
|
|
12
|
+
convertSqlType,
|
|
13
|
+
finaliseExternalTables,
|
|
14
|
+
SqlClients,
|
|
15
|
+
} from "./utils"
|
|
16
|
+
import { DatasourcePlus } from "./base/datasourcePlus"
|
|
10
17
|
|
|
11
18
|
module PostgresModule {
|
|
12
19
|
const { Pool } = require("pg")
|
|
13
20
|
const Sql = require("./base/sql")
|
|
14
|
-
const { FieldTypes } = require("../constants")
|
|
15
|
-
const {
|
|
16
|
-
buildExternalTableId,
|
|
17
|
-
convertType,
|
|
18
|
-
copyExistingPropsOver,
|
|
19
|
-
} = require("./utils")
|
|
20
21
|
const { escapeDangerousCharacters } = require("../utilities")
|
|
21
22
|
|
|
22
23
|
const JSON_REGEX = /'{.*}'::json/s
|
|
@@ -27,6 +28,7 @@ module PostgresModule {
|
|
|
27
28
|
database: string
|
|
28
29
|
user: string
|
|
29
30
|
password: string
|
|
31
|
+
schema: string
|
|
30
32
|
ssl?: boolean
|
|
31
33
|
ca?: string
|
|
32
34
|
rejectUnauthorized?: boolean
|
|
@@ -64,6 +66,11 @@ module PostgresModule {
|
|
|
64
66
|
default: "root",
|
|
65
67
|
required: true,
|
|
66
68
|
},
|
|
69
|
+
schema: {
|
|
70
|
+
type: DatasourceFieldTypes.STRING,
|
|
71
|
+
default: "public",
|
|
72
|
+
required: true,
|
|
73
|
+
},
|
|
67
74
|
ssl: {
|
|
68
75
|
type: DatasourceFieldTypes.BOOLEAN,
|
|
69
76
|
default: false,
|
|
@@ -96,22 +103,6 @@ module PostgresModule {
|
|
|
96
103
|
},
|
|
97
104
|
}
|
|
98
105
|
|
|
99
|
-
const TYPE_MAP = {
|
|
100
|
-
text: FieldTypes.LONGFORM,
|
|
101
|
-
varchar: FieldTypes.STRING,
|
|
102
|
-
integer: FieldTypes.NUMBER,
|
|
103
|
-
bigint: FieldTypes.NUMBER,
|
|
104
|
-
decimal: FieldTypes.NUMBER,
|
|
105
|
-
smallint: FieldTypes.NUMBER,
|
|
106
|
-
real: FieldTypes.NUMBER,
|
|
107
|
-
"double precision": FieldTypes.NUMBER,
|
|
108
|
-
timestamp: FieldTypes.DATETIME,
|
|
109
|
-
time: FieldTypes.DATETIME,
|
|
110
|
-
boolean: FieldTypes.BOOLEAN,
|
|
111
|
-
json: FieldTypes.JSON,
|
|
112
|
-
date: FieldTypes.DATETIME,
|
|
113
|
-
}
|
|
114
|
-
|
|
115
106
|
async function internalQuery(client: any, query: SqlQuery) {
|
|
116
107
|
// need to handle a specific issue with json data types in postgres,
|
|
117
108
|
// new lines inside the JSON data will break it
|
|
@@ -132,13 +123,14 @@ module PostgresModule {
|
|
|
132
123
|
}
|
|
133
124
|
}
|
|
134
125
|
|
|
135
|
-
class PostgresIntegration extends Sql {
|
|
126
|
+
class PostgresIntegration extends Sql implements DatasourcePlus {
|
|
136
127
|
static pool: any
|
|
137
128
|
private readonly client: any
|
|
138
129
|
private readonly config: PostgresConfig
|
|
130
|
+
public tables: Record<string, Table> = {}
|
|
131
|
+
public schemaErrors: Record<string, string> = {}
|
|
139
132
|
|
|
140
|
-
COLUMNS_SQL
|
|
141
|
-
"select * from information_schema.columns where not table_schema = 'information_schema' and not table_schema = 'pg_catalog'"
|
|
133
|
+
COLUMNS_SQL!: string
|
|
142
134
|
|
|
143
135
|
PRIMARY_KEYS_SQL = `
|
|
144
136
|
select tc.table_schema, tc.table_name, kc.column_name as primary_key
|
|
@@ -151,7 +143,7 @@ module PostgresModule {
|
|
|
151
143
|
`
|
|
152
144
|
|
|
153
145
|
constructor(config: PostgresConfig) {
|
|
154
|
-
super(
|
|
146
|
+
super(SqlClients.POSTGRES)
|
|
155
147
|
this.config = config
|
|
156
148
|
|
|
157
149
|
let newConfig = {
|
|
@@ -168,6 +160,17 @@ module PostgresModule {
|
|
|
168
160
|
}
|
|
169
161
|
|
|
170
162
|
this.client = this.pool
|
|
163
|
+
this.setSchema()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
setSchema() {
|
|
167
|
+
if (!this.config.schema) {
|
|
168
|
+
this.config.schema = "public"
|
|
169
|
+
}
|
|
170
|
+
this.client.on("connect", (client: any) => {
|
|
171
|
+
client.query(`SET search_path TO ${this.config.schema}`)
|
|
172
|
+
})
|
|
173
|
+
this.COLUMNS_SQL = `select * from information_schema.columns where table_schema = '${this.config.schema}'`
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
/**
|
|
@@ -207,13 +210,13 @@ module PostgresModule {
|
|
|
207
210
|
if (!tables[tableName] || !tables[tableName].schema) {
|
|
208
211
|
tables[tableName] = {
|
|
209
212
|
_id: buildExternalTableId(datasourceId, tableName),
|
|
210
|
-
primary: tableKeys[tableName] || [
|
|
213
|
+
primary: tableKeys[tableName] || [],
|
|
211
214
|
name: tableName,
|
|
212
215
|
schema: {},
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
218
|
|
|
216
|
-
const type: string =
|
|
219
|
+
const type: string = convertSqlType(column.data_type)
|
|
217
220
|
const identity = !!(
|
|
218
221
|
column.identity_generation ||
|
|
219
222
|
column.identity_start ||
|
|
@@ -232,10 +235,9 @@ module PostgresModule {
|
|
|
232
235
|
}
|
|
233
236
|
}
|
|
234
237
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
this.tables = tables
|
|
238
|
+
const final = finaliseExternalTables(tables, entities)
|
|
239
|
+
this.tables = final.tables
|
|
240
|
+
this.schemaErrors = final.errors
|
|
239
241
|
}
|
|
240
242
|
|
|
241
243
|
async create(query: SqlQuery | string) {
|
|
@@ -261,8 +263,16 @@ module PostgresModule {
|
|
|
261
263
|
async query(json: QueryJson) {
|
|
262
264
|
const operation = this._operation(json).toLowerCase()
|
|
263
265
|
const input = this._query(json)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
+
if (Array.isArray(input)) {
|
|
267
|
+
const responses = []
|
|
268
|
+
for (let query of input) {
|
|
269
|
+
responses.push(await internalQuery(this.client, query))
|
|
270
|
+
}
|
|
271
|
+
return responses
|
|
272
|
+
} else {
|
|
273
|
+
const response = await internalQuery(this.client, input)
|
|
274
|
+
return response.rows.length ? response.rows : [{ [operation]: true }]
|
|
275
|
+
}
|
|
266
276
|
}
|
|
267
277
|
}
|
|
268
278
|
|
package/src/integrations/rest.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
DatasourceFieldTypes,
|
|
4
4
|
QueryTypes,
|
|
5
5
|
} from "../definitions/datasource"
|
|
6
|
+
import { IntegrationBase } from "./base/IntegrationBase"
|
|
6
7
|
|
|
7
8
|
module RestModule {
|
|
8
9
|
const fetch = require("node-fetch")
|
|
@@ -131,7 +132,7 @@ module RestModule {
|
|
|
131
132
|
},
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
class RestIntegration {
|
|
135
|
+
class RestIntegration implements IntegrationBase {
|
|
135
136
|
private config: RestConfig
|
|
136
137
|
private headers: {
|
|
137
138
|
[key: string]: string
|
package/src/integrations/s3.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Integration, QueryTypes } from "../definitions/datasource"
|
|
2
|
+
import { IntegrationBase } from "./base/IntegrationBase"
|
|
2
3
|
|
|
3
4
|
module S3Module {
|
|
4
5
|
const AWS = require("aws-sdk")
|
|
@@ -42,7 +43,7 @@ module S3Module {
|
|
|
42
43
|
},
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
class S3Integration {
|
|
46
|
+
class S3Integration implements IntegrationBase {
|
|
46
47
|
private readonly config: S3Config
|
|
47
48
|
private client: any
|
|
48
49
|
private connectionPromise: Promise<any>
|
|
@@ -9,32 +9,39 @@ class TestConfiguration {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
describe("MS SQL Server Integration", () => {
|
|
12
|
-
let config
|
|
12
|
+
let config
|
|
13
13
|
|
|
14
|
-
beforeEach(() => {
|
|
14
|
+
beforeEach(async () => {
|
|
15
15
|
config = new TestConfiguration()
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
describe("check sql used", () => {
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
await config.integration.connect()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it("calls the create method with the correct params", async () => {
|
|
24
|
+
const sql = "insert into users (name, age) values ('Joe', 123);"
|
|
25
|
+
const response = await config.integration.create({
|
|
26
|
+
sql
|
|
27
|
+
})
|
|
28
|
+
expect(config.integration.client.request).toHaveBeenCalledWith()
|
|
29
|
+
expect(response[0]).toEqual(sql)
|
|
22
30
|
})
|
|
23
|
-
expect(config.integration.client.query).toHaveBeenCalledWith(sql, {})
|
|
24
|
-
})
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
it("calls the read method with the correct params", async () => {
|
|
33
|
+
const sql = "select * from users;"
|
|
34
|
+
const response = await config.integration.read({
|
|
35
|
+
sql
|
|
36
|
+
})
|
|
37
|
+
expect(config.integration.client.request).toHaveBeenCalledWith()
|
|
38
|
+
expect(response[0]).toEqual(sql)
|
|
30
39
|
})
|
|
31
|
-
expect(config.integration.client.query).toHaveBeenCalledWith(sql, {})
|
|
32
40
|
})
|
|
33
41
|
|
|
34
42
|
describe("no rows returned", () => {
|
|
35
43
|
beforeEach(async () => {
|
|
36
44
|
await config.integration.connect()
|
|
37
|
-
config.integration.client.query.mockImplementation(() => ({ rows: [] }))
|
|
38
45
|
})
|
|
39
46
|
|
|
40
47
|
it("returns the correct response when the create response has no rows", async () => {
|
|
@@ -42,7 +49,7 @@ describe("MS SQL Server Integration", () => {
|
|
|
42
49
|
const response = await config.integration.create({
|
|
43
50
|
sql
|
|
44
51
|
})
|
|
45
|
-
expect(response).toEqual(
|
|
52
|
+
expect(response[0]).toEqual(sql)
|
|
46
53
|
})
|
|
47
54
|
})
|
|
48
55
|
})
|
|
@@ -15,6 +15,10 @@ describe("Postgres Integration", () => {
|
|
|
15
15
|
config = new TestConfiguration()
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
+
it("calls the connection callback", async () => {
|
|
19
|
+
expect(pg.on).toHaveBeenCalledWith('connect', expect.anything())
|
|
20
|
+
})
|
|
21
|
+
|
|
18
22
|
it("calls the create method with the correct params", async () => {
|
|
19
23
|
const sql = "insert into users (name, age) values ('Joe', 123);"
|
|
20
24
|
await config.integration.create({
|
|
@@ -1,12 +1,45 @@
|
|
|
1
1
|
import { SqlQuery } from "../definitions/datasource"
|
|
2
|
-
import { Datasource } from "../definitions/common"
|
|
2
|
+
import { Datasource, Table } from "../definitions/common"
|
|
3
3
|
import { SourceNames } from "../definitions/datasource"
|
|
4
4
|
const { DocumentTypes, SEPARATOR } = require("../db/utils")
|
|
5
|
-
const { FieldTypes } = require("../constants")
|
|
5
|
+
const { FieldTypes, BuildSchemaErrors } = require("../constants")
|
|
6
6
|
|
|
7
7
|
const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
|
|
8
8
|
const ROW_ID_REGEX = /^\[.*]$/g
|
|
9
9
|
|
|
10
|
+
const SQL_TYPE_MAP = {
|
|
11
|
+
text: FieldTypes.LONGFORM,
|
|
12
|
+
varchar: FieldTypes.STRING,
|
|
13
|
+
integer: FieldTypes.NUMBER,
|
|
14
|
+
bigint: FieldTypes.NUMBER,
|
|
15
|
+
decimal: FieldTypes.NUMBER,
|
|
16
|
+
smallint: FieldTypes.NUMBER,
|
|
17
|
+
real: FieldTypes.NUMBER,
|
|
18
|
+
"double precision": FieldTypes.NUMBER,
|
|
19
|
+
timestamp: FieldTypes.DATETIME,
|
|
20
|
+
time: FieldTypes.DATETIME,
|
|
21
|
+
boolean: FieldTypes.BOOLEAN,
|
|
22
|
+
json: FieldTypes.JSON,
|
|
23
|
+
date: FieldTypes.DATETIME,
|
|
24
|
+
blob: FieldTypes.LONGFORM,
|
|
25
|
+
enum: FieldTypes.STRING,
|
|
26
|
+
float: FieldTypes.NUMBER,
|
|
27
|
+
int: FieldTypes.NUMBER,
|
|
28
|
+
numeric: FieldTypes.NUMBER,
|
|
29
|
+
mediumint: FieldTypes.NUMBER,
|
|
30
|
+
dec: FieldTypes.NUMBER,
|
|
31
|
+
double: FieldTypes.NUMBER,
|
|
32
|
+
fixed: FieldTypes.NUMBER,
|
|
33
|
+
datetime: FieldTypes.DATETIME,
|
|
34
|
+
tinyint: FieldTypes.BOOLEAN,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export enum SqlClients {
|
|
38
|
+
MS_SQL = "mssql",
|
|
39
|
+
POSTGRES = "pg",
|
|
40
|
+
MY_SQL = "mysql",
|
|
41
|
+
}
|
|
42
|
+
|
|
10
43
|
export function isExternalTable(tableId: string) {
|
|
11
44
|
return tableId.includes(DocumentTypes.DATASOURCE)
|
|
12
45
|
}
|
|
@@ -68,8 +101,8 @@ export function breakRowIdField(_id: string | { _id: string }): any[] {
|
|
|
68
101
|
}
|
|
69
102
|
}
|
|
70
103
|
|
|
71
|
-
export function
|
|
72
|
-
for (let [external, internal] of Object.entries(
|
|
104
|
+
export function convertSqlType(type: string) {
|
|
105
|
+
for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) {
|
|
73
106
|
if (type.toLowerCase().includes(external)) {
|
|
74
107
|
return internal
|
|
75
108
|
}
|
|
@@ -102,14 +135,14 @@ export function isIsoDateString(str: string) {
|
|
|
102
135
|
}
|
|
103
136
|
|
|
104
137
|
// add the existing relationships from the entities if they exist, to prevent them from being overridden
|
|
105
|
-
|
|
138
|
+
function copyExistingPropsOver(
|
|
106
139
|
tableName: string,
|
|
107
|
-
|
|
140
|
+
table: Table,
|
|
108
141
|
entities: { [key: string]: any }
|
|
109
142
|
) {
|
|
110
143
|
if (entities && entities[tableName]) {
|
|
111
144
|
if (entities[tableName].primaryDisplay) {
|
|
112
|
-
|
|
145
|
+
table.primaryDisplay = entities[tableName].primaryDisplay
|
|
113
146
|
}
|
|
114
147
|
const existingTableSchema = entities[tableName].schema
|
|
115
148
|
for (let key in existingTableSchema) {
|
|
@@ -117,8 +150,27 @@ export function copyExistingPropsOver(
|
|
|
117
150
|
continue
|
|
118
151
|
}
|
|
119
152
|
if (existingTableSchema[key].type === "link") {
|
|
120
|
-
|
|
153
|
+
table.schema[key] = existingTableSchema[key]
|
|
121
154
|
}
|
|
122
155
|
}
|
|
123
156
|
}
|
|
157
|
+
return table
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function finaliseExternalTables(
|
|
161
|
+
tables: { [key: string]: any },
|
|
162
|
+
entities: { [key: string]: any }
|
|
163
|
+
) {
|
|
164
|
+
const finalTables: { [key: string]: any } = {}
|
|
165
|
+
const errors: { [key: string]: string } = {}
|
|
166
|
+
for (let [name, table] of Object.entries(tables)) {
|
|
167
|
+
// make sure every table has a key
|
|
168
|
+
if (table.primary == null || table.primary.length === 0) {
|
|
169
|
+
errors[name] = BuildSchemaErrors.NO_KEY
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
172
|
+
// make sure all previous props have been added back
|
|
173
|
+
finalTables[name] = copyExistingPropsOver(name, table, entities)
|
|
174
|
+
}
|
|
175
|
+
return { tables: finalTables, errors }
|
|
124
176
|
}
|
|
@@ -15,7 +15,8 @@ module.exports =
|
|
|
15
15
|
(permType, permLevel = null) =>
|
|
16
16
|
async (ctx, next) => {
|
|
17
17
|
// webhooks don't need authentication, each webhook unique
|
|
18
|
-
|
|
18
|
+
// also internal requests (between services) don't need authorized
|
|
19
|
+
if (isWebhookEndpoint(ctx) || ctx.internal) {
|
|
19
20
|
return next()
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
const CouchDB = require("../db")
|
|
9
9
|
const { DocumentTypes } = require("../db/utils")
|
|
10
10
|
const { PermissionTypes } = require("@budibase/auth/permissions")
|
|
11
|
+
const { app: appCache } = require("@budibase/auth/cache")
|
|
11
12
|
|
|
12
13
|
const DEBOUNCE_TIME_SEC = 30
|
|
13
14
|
|
|
@@ -50,7 +51,9 @@ async function updateAppUpdatedAt(ctx) {
|
|
|
50
51
|
const db = new CouchDB(appId)
|
|
51
52
|
const metadata = await db.get(DocumentTypes.APP_METADATA)
|
|
52
53
|
metadata.updatedAt = new Date().toISOString()
|
|
53
|
-
await db.put(metadata)
|
|
54
|
+
const response = await db.put(metadata)
|
|
55
|
+
metadata._rev = response.rev
|
|
56
|
+
await appCache.invalidateAppMetadata(appId, metadata)
|
|
54
57
|
// set a new debounce record with a short TTL
|
|
55
58
|
await setDebounce(appId, DEBOUNCE_TIME_SEC)
|
|
56
59
|
}
|
|
@@ -311,7 +311,8 @@ class TestConfiguration {
|
|
|
311
311
|
|
|
312
312
|
async createDatasource(config = null) {
|
|
313
313
|
config = config || basicDatasource()
|
|
314
|
-
|
|
314
|
+
const response = await this._req(config, null, controllers.datasource.save)
|
|
315
|
+
this.datasource = response.datasource
|
|
315
316
|
return this.datasource
|
|
316
317
|
}
|
|
317
318
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const actions = require("
|
|
2
|
-
const automationUtils = require("
|
|
1
|
+
const actions = require("../automations/actions")
|
|
2
|
+
const automationUtils = require("../automations/automationUtils")
|
|
3
3
|
const AutomationEmitter = require("../events/AutomationEmitter")
|
|
4
4
|
const { processObject } = require("@budibase/string-templates")
|
|
5
5
|
const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants
|
|
@@ -8,8 +8,10 @@ const { DocumentTypes, isDevAppID } = require("../db/utils")
|
|
|
8
8
|
const { doInTenant } = require("@budibase/auth/tenancy")
|
|
9
9
|
const env = require("../environment")
|
|
10
10
|
const usage = require("../utilities/usageQuota")
|
|
11
|
+
const { definitions: triggerDefs } = require("../automations/triggerInfo")
|
|
11
12
|
|
|
12
13
|
const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
|
|
14
|
+
const CRON_STEP_ID = triggerDefs.CRON.stepId
|
|
13
15
|
const STOPPED_STATUS = { success: false, status: "STOPPED" }
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -23,6 +25,8 @@ class Orchestrator {
|
|
|
23
25
|
this._chainCount = this._metadata ? this._metadata.automationChainCount : 0
|
|
24
26
|
this._appId = triggerOutput.appId
|
|
25
27
|
this._app = null
|
|
28
|
+
const triggerStepId = automation.definition.trigger.stepId
|
|
29
|
+
triggerOutput = this.cleanupTriggerOutputs(triggerStepId, triggerOutput)
|
|
26
30
|
// remove from context
|
|
27
31
|
delete triggerOutput.appId
|
|
28
32
|
delete triggerOutput.metadata
|
|
@@ -34,11 +38,17 @@ class Orchestrator {
|
|
|
34
38
|
this._emitter = new AutomationEmitter(this._chainCount + 1)
|
|
35
39
|
this.executionOutput = { trigger: {}, steps: [] }
|
|
36
40
|
// setup the execution output
|
|
37
|
-
const triggerStepId = automation.definition.trigger.stepId
|
|
38
41
|
const triggerId = automation.definition.trigger.id
|
|
39
42
|
this.updateExecutionOutput(triggerId, triggerStepId, null, triggerOutput)
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
cleanupTriggerOutputs(stepId, triggerOutput) {
|
|
46
|
+
if (stepId === CRON_STEP_ID) {
|
|
47
|
+
triggerOutput.timestamp = Date.now()
|
|
48
|
+
}
|
|
49
|
+
return triggerOutput
|
|
50
|
+
}
|
|
51
|
+
|
|
42
52
|
async getStepFunctionality(stepId) {
|
|
43
53
|
let step = await actions.getAction(stepId)
|
|
44
54
|
if (step == null) {
|
|
@@ -119,10 +129,17 @@ class Orchestrator {
|
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
131
|
|
|
122
|
-
module.exports =
|
|
132
|
+
module.exports = (input, callback) => {
|
|
123
133
|
const automationOrchestrator = new Orchestrator(
|
|
124
|
-
|
|
125
|
-
|
|
134
|
+
input.data.automation,
|
|
135
|
+
input.data.event
|
|
126
136
|
)
|
|
127
|
-
|
|
137
|
+
automationOrchestrator
|
|
138
|
+
.execute()
|
|
139
|
+
.then(response => {
|
|
140
|
+
callback(null, response)
|
|
141
|
+
})
|
|
142
|
+
.catch(err => {
|
|
143
|
+
callback(err)
|
|
144
|
+
})
|
|
128
145
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const workerFarm = require("worker-farm")
|
|
2
|
+
const env = require("../environment")
|
|
3
|
+
|
|
4
|
+
const ThreadType = {
|
|
5
|
+
QUERY: "query",
|
|
6
|
+
AUTOMATION: "automation",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function typeToFile(type) {
|
|
10
|
+
let filename = null
|
|
11
|
+
switch (type) {
|
|
12
|
+
case ThreadType.QUERY:
|
|
13
|
+
filename = "./query"
|
|
14
|
+
break
|
|
15
|
+
case ThreadType.AUTOMATION:
|
|
16
|
+
filename = "./automation"
|
|
17
|
+
break
|
|
18
|
+
default:
|
|
19
|
+
throw "Unknown thread type"
|
|
20
|
+
}
|
|
21
|
+
return require.resolve(filename)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class Thread {
|
|
25
|
+
constructor(type, opts = { timeoutMs: null, count: 1 }) {
|
|
26
|
+
this.type = type
|
|
27
|
+
if (!env.isTest()) {
|
|
28
|
+
const workerOpts = {
|
|
29
|
+
autoStart: true,
|
|
30
|
+
maxConcurrentWorkers: opts.count ? opts.count : 1,
|
|
31
|
+
}
|
|
32
|
+
if (opts.timeoutMs) {
|
|
33
|
+
workerOpts.maxCallTime = opts.timeoutMs
|
|
34
|
+
}
|
|
35
|
+
this.workers = workerFarm(workerOpts, typeToFile(type))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
run(data) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
let fncToCall
|
|
42
|
+
// if in test then don't use threading
|
|
43
|
+
if (env.isTest()) {
|
|
44
|
+
fncToCall = require(typeToFile(this.type))
|
|
45
|
+
} else {
|
|
46
|
+
fncToCall = this.workers
|
|
47
|
+
}
|
|
48
|
+
fncToCall(data, (err, response) => {
|
|
49
|
+
if (err) {
|
|
50
|
+
reject(err)
|
|
51
|
+
} else {
|
|
52
|
+
resolve(response)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports.Thread = Thread
|
|
60
|
+
module.exports.ThreadType = ThreadType
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const ScriptRunner = require("../utilities/scriptRunner")
|
|
2
|
+
const { integrations } = require("../integrations")
|
|
3
|
+
|
|
4
|
+
function formatResponse(resp) {
|
|
5
|
+
if (typeof resp === "string") {
|
|
6
|
+
try {
|
|
7
|
+
resp = JSON.parse(resp)
|
|
8
|
+
} catch (err) {
|
|
9
|
+
resp = { response: resp }
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return resp
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function runAndTransform(datasource, queryVerb, query, transformer) {
|
|
16
|
+
const Integration = integrations[datasource.source]
|
|
17
|
+
if (!Integration) {
|
|
18
|
+
throw "Integration type does not exist."
|
|
19
|
+
}
|
|
20
|
+
const integration = new Integration(datasource.config)
|
|
21
|
+
|
|
22
|
+
let rows = formatResponse(await integration[queryVerb](query))
|
|
23
|
+
|
|
24
|
+
// transform as required
|
|
25
|
+
if (transformer) {
|
|
26
|
+
const runner = new ScriptRunner(transformer, { data: rows })
|
|
27
|
+
rows = runner.execute()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// needs to an array for next step
|
|
31
|
+
if (!Array.isArray(rows)) {
|
|
32
|
+
rows = [rows]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// map into JSON if just raw primitive here
|
|
36
|
+
if (rows.find(row => typeof row !== "object")) {
|
|
37
|
+
rows = rows.map(value => ({ value }))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// get all the potential fields in the schema
|
|
41
|
+
let keys = rows.flatMap(Object.keys)
|
|
42
|
+
|
|
43
|
+
if (integration.end) {
|
|
44
|
+
integration.end()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { rows, keys }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = (input, callback) => {
|
|
51
|
+
runAndTransform(
|
|
52
|
+
input.datasource,
|
|
53
|
+
input.queryVerb,
|
|
54
|
+
input.query,
|
|
55
|
+
input.transformer
|
|
56
|
+
)
|
|
57
|
+
.then(response => {
|
|
58
|
+
callback(null, response)
|
|
59
|
+
})
|
|
60
|
+
.catch(err => {
|
|
61
|
+
callback(err)
|
|
62
|
+
})
|
|
63
|
+
}
|
|
@@ -51,7 +51,7 @@ function parse(csvString, parsers) {
|
|
|
51
51
|
})
|
|
52
52
|
result.subscribe(row => {
|
|
53
53
|
// For each CSV row parse all the columns that need parsed
|
|
54
|
-
for (let key
|
|
54
|
+
for (let key of Object.keys(parsers)) {
|
|
55
55
|
if (!schema[key] || schema[key].success) {
|
|
56
56
|
// get the validator for the column type
|
|
57
57
|
const validator = VALIDATORS[parsers[key].type]
|
|
@@ -76,16 +76,58 @@ function parse(csvString, parsers) {
|
|
|
76
76
|
})
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
function updateSchema({ schema, existingTable }) {
|
|
80
|
+
if (!schema) {
|
|
81
|
+
return schema
|
|
82
|
+
}
|
|
83
|
+
const finalSchema = {}
|
|
84
|
+
const schemaKeyMap = {}
|
|
85
|
+
Object.keys(schema).forEach(key => (schemaKeyMap[key.toLowerCase()] = key))
|
|
86
|
+
for (let [key, field] of Object.entries(existingTable.schema)) {
|
|
87
|
+
const lcKey = key.toLowerCase()
|
|
88
|
+
const foundKey = schemaKeyMap[lcKey]
|
|
89
|
+
if (foundKey) {
|
|
90
|
+
finalSchema[key] = schema[foundKey]
|
|
91
|
+
finalSchema[key].type = field.type
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return finalSchema
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function transform({ schema, csvString, existingTable }) {
|
|
80
98
|
const colParser = {}
|
|
81
99
|
|
|
82
|
-
|
|
100
|
+
// make sure the table has all the columns required for import
|
|
101
|
+
if (existingTable) {
|
|
102
|
+
schema = updateSchema({ schema, existingTable })
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (let key of Object.keys(schema)) {
|
|
83
106
|
colParser[key] = PARSERS[schema[key].type] || schema[key].type
|
|
84
107
|
}
|
|
85
108
|
|
|
86
109
|
try {
|
|
87
|
-
const
|
|
88
|
-
|
|
110
|
+
const data = await csv({ colParser }).fromString(csvString)
|
|
111
|
+
const schemaKeyMap = {}
|
|
112
|
+
Object.keys(schema).forEach(key => (schemaKeyMap[key.toLowerCase()] = key))
|
|
113
|
+
for (let element of data) {
|
|
114
|
+
if (!data) {
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
for (let key of Object.keys(element)) {
|
|
118
|
+
const mappedKey = schemaKeyMap[key.toLowerCase()]
|
|
119
|
+
// isn't a column in the table, remove it
|
|
120
|
+
if (mappedKey == null) {
|
|
121
|
+
delete element[key]
|
|
122
|
+
}
|
|
123
|
+
// casing is different, fix it in row
|
|
124
|
+
else if (key !== mappedKey) {
|
|
125
|
+
element[mappedKey] = element[key]
|
|
126
|
+
delete element[key]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return data
|
|
89
131
|
} catch (err) {
|
|
90
132
|
console.error(`Error transforming CSV to JSON for data import`, err)
|
|
91
133
|
throw err
|
|
@@ -95,4 +137,5 @@ async function transform({ schema, csvString }) {
|
|
|
95
137
|
module.exports = {
|
|
96
138
|
parse,
|
|
97
139
|
transform,
|
|
140
|
+
updateSchema,
|
|
98
141
|
}
|