@budibase/server 0.9.185-alpha.13 → 0.9.185-alpha.17

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.
Files changed (199) hide show
  1. package/Dockerfile +7 -1
  2. package/__mocks__/oracledb.ts +31 -0
  3. package/builder/assets/{index.4a7c5636.css → index.67ac3a72.css} +1 -1
  4. package/builder/assets/{index.dc495306.js → index.a8407653.js} +176 -175
  5. package/builder/assets/oracle.66592424.png +0 -0
  6. package/builder/index.html +2 -2
  7. package/coverage/clover.xml +239 -230
  8. package/coverage/coverage-final.json +7 -7
  9. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/application.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/index.html +1 -1
  21. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/query.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/external.js.html +29 -8
  37. package/coverage/lcov-report/api/controllers/table/index.html +13 -13
  38. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  46. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  48. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  49. package/coverage/lcov-report/api/index.html +1 -1
  50. package/coverage/lcov-report/api/index.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/index.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  81. package/coverage/lcov-report/automations/actions.js.html +1 -1
  82. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  83. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  84. package/coverage/lcov-report/automations/index.html +1 -1
  85. package/coverage/lcov-report/automations/index.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/index.html +1 -1
  95. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  103. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  111. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  112. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  113. package/coverage/lcov-report/automations/utils.js.html +1 -1
  114. package/coverage/lcov-report/constants/index.html +1 -1
  115. package/coverage/lcov-report/constants/index.js.html +22 -22
  116. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  117. package/coverage/lcov-report/constants/screens.js.html +1 -1
  118. package/coverage/lcov-report/db/client.js.html +1 -1
  119. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  120. package/coverage/lcov-report/db/index.html +1 -1
  121. package/coverage/lcov-report/db/index.js.html +1 -1
  122. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  124. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  125. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  126. package/coverage/lcov-report/db/newid.js.html +3 -3
  127. package/coverage/lcov-report/db/utils.js.html +57 -57
  128. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  129. package/coverage/lcov-report/events/index.html +1 -1
  130. package/coverage/lcov-report/events/index.js.html +1 -1
  131. package/coverage/lcov-report/events/utils.js.html +1 -1
  132. package/coverage/lcov-report/index.html +23 -23
  133. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  134. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  135. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  136. package/coverage/lcov-report/middleware/authorized.js.html +1 -1
  137. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  138. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  139. package/coverage/lcov-report/middleware/index.html +1 -1
  140. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  141. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  142. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  143. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  144. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  145. package/coverage/lcov-report/threads/automation.js.html +14 -8
  146. package/coverage/lcov-report/threads/index.html +27 -27
  147. package/coverage/lcov-report/threads/index.js.html +29 -11
  148. package/coverage/lcov-report/threads/query.js.html +16 -7
  149. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  150. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  151. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  152. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  153. package/coverage/lcov-report/utilities/global.js.html +1 -1
  154. package/coverage/lcov-report/utilities/index.html +1 -1
  155. package/coverage/lcov-report/utilities/index.js.html +1 -1
  156. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  157. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  158. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  159. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  160. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  161. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  162. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  163. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  164. package/coverage/lcov-report/utilities/security.js.html +1 -1
  165. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  166. package/coverage/lcov-report/utilities/users.js.html +1 -1
  167. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  168. package/coverage/lcov.info +328 -309
  169. package/dist/api/controllers/table/external.js +8 -1
  170. package/dist/definitions/datasource.js +1 -0
  171. package/dist/environment.js +13 -0
  172. package/dist/integrations/base/sql.js +4 -0
  173. package/dist/integrations/index.js +6 -0
  174. package/dist/integrations/oracle.js +345 -0
  175. package/dist/integrations/utils.js +10 -1
  176. package/dist/package.json +8 -4
  177. package/dist/threads/automation.js +3 -1
  178. package/dist/threads/index.js +9 -3
  179. package/dist/threads/query.js +3 -0
  180. package/dist/tsconfig.tsbuildinfo +1 -1
  181. package/package.json +9 -5
  182. package/scripts/dev/manage.js +1 -0
  183. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  184. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  185. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  186. package/scripts/integrations/oracle/oracle.md +90 -0
  187. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  188. package/src/api/controllers/table/external.js +8 -1
  189. package/src/definitions/datasource.ts +1 -0
  190. package/src/environment.js +15 -0
  191. package/src/integrations/base/IntegrationBase.ts +4 -4
  192. package/src/integrations/base/sql.ts +3 -0
  193. package/src/integrations/index.ts +7 -0
  194. package/src/integrations/oracle.ts +445 -0
  195. package/src/integrations/tests/oracle.spec.js +94 -0
  196. package/src/integrations/utils.ts +10 -1
  197. package/src/threads/automation.js +3 -1
  198. package/src/threads/index.js +9 -3
  199. package/src/threads/query.js +3 -0
@@ -0,0 +1,445 @@
1
+ import {
2
+ Integration,
3
+ DatasourceFieldTypes,
4
+ QueryTypes,
5
+ SqlQuery,
6
+ QueryJson,
7
+ Operation,
8
+ } from "../definitions/datasource"
9
+ import {
10
+ finaliseExternalTables,
11
+ getSqlQuery,
12
+ buildExternalTableId,
13
+ convertSqlType,
14
+ SqlClients,
15
+ } from "./utils"
16
+ import oracledb, {
17
+ ExecuteOptions,
18
+ Result,
19
+ Connection,
20
+ ConnectionAttributes,
21
+ BindParameters,
22
+ } from "oracledb"
23
+ import Sql from "./base/sql"
24
+ import { Table } from "../definitions/common"
25
+ import { DatasourcePlus } from "./base/datasourcePlus"
26
+ import { FieldTypes } from "../constants"
27
+
28
+ module OracleModule {
29
+ oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT
30
+
31
+ interface OracleConfig {
32
+ host: string
33
+ port: number
34
+ database: string
35
+ user: string
36
+ password: string
37
+ }
38
+
39
+ const SCHEMA: Integration = {
40
+ docs: "https://github.com/oracle/node-oracledb",
41
+ plus: true,
42
+ friendlyName: "Oracle",
43
+ description:
44
+ "Oracle Database is an object-relational database management system developed by Oracle Corporation",
45
+ datasource: {
46
+ host: {
47
+ type: DatasourceFieldTypes.STRING,
48
+ default: "localhost",
49
+ required: true,
50
+ },
51
+ port: {
52
+ type: DatasourceFieldTypes.NUMBER,
53
+ required: true,
54
+ default: 1521,
55
+ },
56
+ database: {
57
+ type: DatasourceFieldTypes.STRING,
58
+ required: true,
59
+ },
60
+ user: {
61
+ type: DatasourceFieldTypes.STRING,
62
+ required: true,
63
+ },
64
+ password: {
65
+ type: DatasourceFieldTypes.PASSWORD,
66
+ required: true,
67
+ },
68
+ },
69
+ query: {
70
+ create: {
71
+ type: QueryTypes.SQL,
72
+ },
73
+ read: {
74
+ type: QueryTypes.SQL,
75
+ },
76
+ update: {
77
+ type: QueryTypes.SQL,
78
+ },
79
+ delete: {
80
+ type: QueryTypes.SQL,
81
+ },
82
+ },
83
+ }
84
+
85
+ const UNSUPPORTED_TYPES = ["BLOB", "CLOB", "NCLOB"]
86
+
87
+ /**
88
+ * Raw query response
89
+ */
90
+ interface ColumnsResponse {
91
+ TABLE_NAME: string
92
+ COLUMN_NAME: string
93
+ DATA_TYPE: string
94
+ DATA_DEFAULT: string | null
95
+ COLUMN_ID: number
96
+ CONSTRAINT_NAME: string | null
97
+ CONSTRAINT_TYPE: string | null
98
+ R_CONSTRAINT_NAME: string | null
99
+ SEARCH_CONDITION: string | null
100
+ }
101
+
102
+ /**
103
+ * An oracle constraint
104
+ */
105
+ interface OracleConstraint {
106
+ name: string
107
+ type: string
108
+ relatedConstraintName: string | null
109
+ searchCondition: string | null
110
+ }
111
+
112
+ /**
113
+ * An oracle column and it's related constraints
114
+ */
115
+ interface OracleColumn {
116
+ name: string
117
+ type: string
118
+ default: string | null
119
+ id: number
120
+ constraints: { [key: string]: OracleConstraint }
121
+ }
122
+
123
+ /**
124
+ * An oracle table and it's related columns
125
+ */
126
+ interface OracleTable {
127
+ name: string
128
+ columns: { [key: string]: OracleColumn }
129
+ }
130
+
131
+ const OracleContraintTypes = {
132
+ PRIMARY: "P",
133
+ NOT_NULL_OR_CHECK: "C",
134
+ FOREIGN_KEY: "R",
135
+ UNIQUE: "U",
136
+ }
137
+
138
+ class OracleIntegration extends Sql implements DatasourcePlus {
139
+ private readonly config: OracleConfig
140
+
141
+ public tables: Record<string, Table> = {}
142
+ public schemaErrors: Record<string, string> = {}
143
+
144
+ private readonly COLUMNS_SQL = `
145
+ SELECT
146
+ tabs.table_name,
147
+ cols.column_name,
148
+ cols.data_type,
149
+ cols.data_default,
150
+ cols.column_id,
151
+ cons.constraint_name,
152
+ cons.constraint_type,
153
+ cons.r_constraint_name,
154
+ cons.search_condition
155
+ FROM
156
+ user_tables tabs
157
+ JOIN
158
+ user_tab_columns cols
159
+ ON tabs.table_name = cols.table_name
160
+ LEFT JOIN
161
+ user_cons_columns col_cons
162
+ ON cols.column_name = col_cons.column_name
163
+ AND cols.table_name = col_cons.table_name
164
+ LEFT JOIN
165
+ user_constraints cons
166
+ ON col_cons.constraint_name = cons.constraint_name
167
+ AND cons.table_name = cols.table_name
168
+ WHERE
169
+ (cons.status = 'ENABLED'
170
+ OR cons.status IS NULL)
171
+ `
172
+ constructor(config: OracleConfig) {
173
+ super(SqlClients.ORACLE)
174
+ this.config = config
175
+ }
176
+
177
+ /**
178
+ * Map the flat tabular columns and constraints data into a nested object
179
+ */
180
+ private mapColumns(result: Result<ColumnsResponse>): {
181
+ [key: string]: OracleTable
182
+ } {
183
+ const oracleTables: { [key: string]: OracleTable } = {}
184
+
185
+ if (result.rows) {
186
+ result.rows.forEach(row => {
187
+ const tableName = row.TABLE_NAME
188
+ const columnName = row.COLUMN_NAME
189
+ const dataType = row.DATA_TYPE
190
+ const dataDefault = row.DATA_DEFAULT
191
+ const columnId = row.COLUMN_ID
192
+ const constraintName = row.CONSTRAINT_NAME
193
+ const constraintType = row.CONSTRAINT_TYPE
194
+ const relatedConstraintName = row.R_CONSTRAINT_NAME
195
+ const searchCondition = row.SEARCH_CONDITION
196
+
197
+ let table = oracleTables[tableName]
198
+ if (!table) {
199
+ table = {
200
+ name: tableName,
201
+ columns: {},
202
+ }
203
+ oracleTables[tableName] = table
204
+ }
205
+
206
+ let column = table.columns[columnName]
207
+ if (!column) {
208
+ column = {
209
+ name: columnName,
210
+ type: dataType,
211
+ default: dataDefault,
212
+ id: columnId,
213
+ constraints: {},
214
+ }
215
+ table.columns[columnName] = column
216
+ }
217
+
218
+ if (constraintName && constraintType) {
219
+ let constraint = column.constraints[constraintName]
220
+ if (!constraint) {
221
+ constraint = {
222
+ name: constraintName,
223
+ type: constraintType,
224
+ relatedConstraintName: relatedConstraintName,
225
+ searchCondition: searchCondition,
226
+ }
227
+ }
228
+ column.constraints[constraintName] = constraint
229
+ }
230
+ })
231
+ }
232
+
233
+ return oracleTables
234
+ }
235
+
236
+ private isSupportedColumn(column: OracleColumn) {
237
+ if (UNSUPPORTED_TYPES.includes(column.type)) {
238
+ return false
239
+ }
240
+
241
+ return true
242
+ }
243
+
244
+ private isAutoColumn(column: OracleColumn) {
245
+ if (column.default && column.default.toLowerCase().includes("nextval")) {
246
+ return true
247
+ }
248
+
249
+ return false
250
+ }
251
+
252
+ /**
253
+ * No native boolean in oracle. Best we can do is to check if a manual 1 or 0 number constraint has been set up
254
+ * This matches the default behaviour for generating DDL used in knex.
255
+ */
256
+ private isBooleanType(column: OracleColumn): boolean {
257
+ if (
258
+ column.type.toLowerCase() === "number" &&
259
+ Object.values(column.constraints).filter(c => {
260
+ if (
261
+ c.type === OracleContraintTypes.NOT_NULL_OR_CHECK &&
262
+ c.searchCondition
263
+ ) {
264
+ const condition = c.searchCondition
265
+ .replace(/\s/g, "") // remove spaces
266
+ .replace(/[']+/g, "") // remove quotes
267
+ if (
268
+ condition.includes("in(0,1)") ||
269
+ condition.includes("in(1,0)")
270
+ ) {
271
+ return true
272
+ }
273
+ }
274
+ return false
275
+ }).length > 0
276
+ ) {
277
+ return true
278
+ }
279
+
280
+ return false
281
+ }
282
+
283
+ private internalConvertType(column: OracleColumn): string {
284
+ if (this.isBooleanType(column)) {
285
+ return FieldTypes.BOOLEAN
286
+ }
287
+
288
+ return convertSqlType(column.type)
289
+ }
290
+
291
+ /**
292
+ * Fetches the tables from the oracle table and assigns them to the datasource.
293
+ * @param {*} datasourceId - datasourceId to fetch
294
+ * @param entities - the tables that are to be built
295
+ */
296
+ async buildSchema(datasourceId: string, entities: Record<string, Table>) {
297
+ const columnsResponse = await this.internalQuery<ColumnsResponse>({
298
+ sql: this.COLUMNS_SQL,
299
+ })
300
+ const oracleTables = this.mapColumns(columnsResponse)
301
+
302
+ const tables: { [key: string]: Table } = {}
303
+
304
+ // iterate each table
305
+ Object.values(oracleTables).forEach(oracleTable => {
306
+ let table = tables[oracleTable.name]
307
+ if (!table) {
308
+ table = {
309
+ _id: buildExternalTableId(datasourceId, oracleTable.name),
310
+ primary: [],
311
+ name: oracleTable.name,
312
+ schema: {},
313
+ }
314
+ tables[oracleTable.name] = table
315
+ }
316
+
317
+ // iterate each column on the table
318
+ Object.values(oracleTable.columns)
319
+ // remove columns that we can't read / save
320
+ .filter(oracleColumn => this.isSupportedColumn(oracleColumn))
321
+ // match the order of the columns in the db
322
+ .sort((c1, c2) => c1.id - c2.id)
323
+ .forEach(oracleColumn => {
324
+ const columnName = oracleColumn.name
325
+ let fieldSchema = table.schema[columnName]
326
+ if (!fieldSchema) {
327
+ fieldSchema = {
328
+ autocolumn: this.isAutoColumn(oracleColumn),
329
+ name: columnName,
330
+ type: this.internalConvertType(oracleColumn),
331
+ }
332
+ table.schema[columnName] = fieldSchema
333
+ }
334
+
335
+ // iterate each constraint on the column
336
+ Object.values(oracleColumn.constraints).forEach(
337
+ oracleConstraint => {
338
+ if (oracleConstraint.type === OracleContraintTypes.PRIMARY) {
339
+ table.primary!.push(columnName)
340
+ }
341
+ }
342
+ )
343
+ })
344
+ })
345
+
346
+ const final = finaliseExternalTables(tables, entities)
347
+ this.tables = final.tables
348
+ this.schemaErrors = final.errors
349
+ }
350
+
351
+ /**
352
+ * Knex default returning behaviour does not work with oracle
353
+ * Manually add the behaviour for the return column
354
+ */
355
+ private addReturning(query: SqlQuery, bindings: BindParameters, returnColumn: string) {
356
+ if (bindings instanceof Array) {
357
+ bindings.push({ dir: oracledb.BIND_OUT })
358
+ query.sql = query.sql + ` returning \"${returnColumn}\" into :${bindings.length}`
359
+ }
360
+ }
361
+
362
+ private async internalQuery<T>(query: SqlQuery, returnColum?: string, operation?: string): Promise<Result<T>> {
363
+ let connection
364
+ try {
365
+ connection = await this.getConnection()
366
+
367
+ const options: ExecuteOptions = { autoCommit: true }
368
+ const bindings: BindParameters = query.bindings || []
369
+
370
+ if (returnColum && (operation === Operation.CREATE || operation === Operation.UPDATE)) {
371
+ this.addReturning(query, bindings, returnColum)
372
+ }
373
+
374
+ const result: Result<T> = await connection.execute<T>(
375
+ query.sql,
376
+ bindings,
377
+ options
378
+ )
379
+
380
+ return result
381
+ } finally {
382
+ if (connection) {
383
+ try {
384
+ await connection.close()
385
+ } catch (err) {
386
+ console.error(err)
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ private getConnection = async (): Promise<Connection> => {
393
+ //connectString : "(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))(CONNECT_DATA =(SID= ORCL)))"
394
+ const connectString = `${this.config.host}:${this.config.port || 1521}/${
395
+ this.config.database
396
+ }`
397
+ const attributes: ConnectionAttributes = {
398
+ user: this.config.user,
399
+ password: this.config.user,
400
+ connectString,
401
+ }
402
+ return oracledb.getConnection(attributes)
403
+ }
404
+
405
+ async create(query: SqlQuery | string): Promise<any[]> {
406
+ const response = await this.internalQuery<any>(getSqlQuery(query))
407
+ return response.rows && response.rows.length
408
+ ? response.rows
409
+ : [{ created: true }]
410
+ }
411
+
412
+ async read(query: SqlQuery | string): Promise<any[]> {
413
+ const response = await this.internalQuery<any>(getSqlQuery(query))
414
+ return response.rows ? response.rows : []
415
+ }
416
+
417
+ async update(query: SqlQuery | string): Promise<any[]> {
418
+ const response = await this.internalQuery(getSqlQuery(query))
419
+ return response.rows && response.rows.length
420
+ ? response.rows
421
+ : [{ updated: true }]
422
+ }
423
+
424
+ async delete(query: SqlQuery | string): Promise<any[]> {
425
+ const response = await this.internalQuery(getSqlQuery(query))
426
+ return response.rows && response.rows.length
427
+ ? response.rows
428
+ : [{ deleted: true }]
429
+ }
430
+
431
+ async query(json: QueryJson) {
432
+ const primaryKeys = json.meta!.table!.primary
433
+ const primaryKey = primaryKeys ? primaryKeys[0] : undefined
434
+ const queryFn = (query: any, operation: string) => this.internalQuery(query, primaryKey, operation)
435
+ const processFn = (response: any) => response.rows ? response.rows : []
436
+ const output = await this.queryWithReturning(json, queryFn, processFn)
437
+ return output
438
+ }
439
+ }
440
+
441
+ module.exports = {
442
+ schema: SCHEMA,
443
+ integration: OracleIntegration,
444
+ }
445
+ }
@@ -0,0 +1,94 @@
1
+ const oracledb = require("oracledb")
2
+ const OracleIntegration = require("../oracle")
3
+ jest.mock("oracledb")
4
+
5
+ class TestConfiguration {
6
+ constructor(config = {}) {
7
+ this.integration = new OracleIntegration.integration(config)
8
+ }
9
+ }
10
+
11
+ const options = { autoCommit: true }
12
+
13
+ describe("Oracle Integration", () => {
14
+ let config
15
+
16
+ beforeEach(() => {
17
+ jest.clearAllMocks()
18
+ config = new TestConfiguration()
19
+ })
20
+
21
+ afterEach(() => {
22
+ expect(oracledb.closeMock).toHaveBeenCalled()
23
+ expect(oracledb.closeMock).toHaveBeenCalledTimes(1)
24
+ })
25
+
26
+ it("calls the create method with the correct params", async () => {
27
+ const sql = "insert into users (name, age) values ('Joe', 123);"
28
+ await config.integration.create({
29
+ sql
30
+ })
31
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
32
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
33
+ })
34
+
35
+ it("calls the read method with the correct params", async () => {
36
+ const sql = "select * from users;"
37
+ await config.integration.read({
38
+ sql
39
+ })
40
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
41
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
42
+ })
43
+
44
+ it("calls the update method with the correct params", async () => {
45
+ const sql = "update table users set name = 'test';"
46
+ const response = await config.integration.update({
47
+ sql
48
+ })
49
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
50
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
51
+ })
52
+
53
+ it("calls the delete method with the correct params", async () => {
54
+ const sql = "delete from users where name = 'todelete';"
55
+ await config.integration.delete({
56
+ sql
57
+ })
58
+ expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
59
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
60
+ })
61
+
62
+ describe("no rows returned", () => {
63
+ beforeEach(() => {
64
+ oracledb.executeMock.mockImplementation(() => ({ rows: [] }))
65
+ })
66
+
67
+ it("returns the correct response when the create response has no rows", async () => {
68
+ const sql = "insert into users (name, age) values ('Joe', 123);"
69
+ const response = await config.integration.create({
70
+ sql
71
+ })
72
+ expect(response).toEqual([{ created: true }])
73
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
74
+ })
75
+
76
+ it("returns the correct response when the update response has no rows", async () => {
77
+ const sql = "update table users set name = 'test';"
78
+ const response = await config.integration.update({
79
+ sql
80
+ })
81
+ expect(response).toEqual([{ updated: true }])
82
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
83
+ })
84
+
85
+ it("returns the correct response when the delete response has no rows", async () => {
86
+ const sql = "delete from users where name = 'todelete';"
87
+ const response = await config.integration.delete({
88
+ sql
89
+ })
90
+ expect(response).toEqual([{ deleted: true }])
91
+ expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
92
+ })
93
+ })
94
+ })
@@ -32,12 +32,17 @@ const SQL_TYPE_MAP = {
32
32
  fixed: FieldTypes.NUMBER,
33
33
  datetime: FieldTypes.DATETIME,
34
34
  tinyint: FieldTypes.BOOLEAN,
35
+ long: FieldTypes.LONGFORM,
36
+ number: FieldTypes.NUMBER,
37
+ binary_float: FieldTypes.NUMBER,
38
+ binary_double: FieldTypes.NUMBER,
35
39
  }
36
40
 
37
41
  export enum SqlClients {
38
42
  MS_SQL = "mssql",
39
43
  POSTGRES = "pg",
40
44
  MY_SQL = "mysql",
45
+ ORACLE = "oracledb"
41
46
  }
42
47
 
43
48
  export function isExternalTable(tableId: string) {
@@ -162,7 +167,7 @@ export function finaliseExternalTables(
162
167
  entities: { [key: string]: any }
163
168
  ) {
164
169
  const invalidColumns = Object.values(InvalidColumns)
165
- const finalTables: { [key: string]: any } = {}
170
+ let finalTables: { [key: string]: any } = {}
166
171
  const errors: { [key: string]: string } = {}
167
172
  for (let [name, table] of Object.entries(tables)) {
168
173
  const schemaFields = Object.keys(table.schema)
@@ -177,5 +182,9 @@ export function finaliseExternalTables(
177
182
  // make sure all previous props have been added back
178
183
  finalTables[name] = copyExistingPropsOver(name, table, entities)
179
184
  }
185
+ // sort the tables by name
186
+ finalTables = Object.entries(finalTables)
187
+ .sort(([a], [b]) => a.localeCompare(b))
188
+ .reduce((r, [k, v]) => ({ ...r, [k]: v }), {})
180
189
  return { tables: finalTables, errors }
181
190
  }
@@ -1,3 +1,6 @@
1
+ // when thread starts, make sure it is recorded
2
+ const env = require("../environment")
3
+ env.setInThread()
1
4
  const actions = require("../automations/actions")
2
5
  const automationUtils = require("../automations/automationUtils")
3
6
  const AutomationEmitter = require("../events/AutomationEmitter")
@@ -6,7 +9,6 @@ const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants
6
9
  const CouchDB = require("../db")
7
10
  const { DocumentTypes, isDevAppID } = require("../db/utils")
8
11
  const { doInTenant } = require("@budibase/auth/tenancy")
9
- const env = require("../environment")
10
12
  const usage = require("../utilities/usageQuota")
11
13
  const { definitions: triggerDefs } = require("../automations/triggerInfo")
12
14
 
@@ -24,10 +24,16 @@ function typeToFile(type) {
24
24
  class Thread {
25
25
  constructor(type, opts = { timeoutMs: null, count: 1 }) {
26
26
  this.type = type
27
- if (!env.isTest()) {
27
+ this.count = opts.count ? opts.count : 1
28
+ this.disableThreading =
29
+ env.isTest() ||
30
+ env.DISABLE_THREADING ||
31
+ this.count === 0 ||
32
+ env.isInThread()
33
+ if (!this.disableThreading) {
28
34
  const workerOpts = {
29
35
  autoStart: true,
30
- maxConcurrentWorkers: opts.count ? opts.count : 1,
36
+ maxConcurrentWorkers: this.count,
31
37
  }
32
38
  if (opts.timeoutMs) {
33
39
  workerOpts.maxCallTime = opts.timeoutMs
@@ -40,7 +46,7 @@ class Thread {
40
46
  return new Promise((resolve, reject) => {
41
47
  let fncToCall
42
48
  // if in test then don't use threading
43
- if (env.isTest()) {
49
+ if (this.disableThreading) {
44
50
  fncToCall = require(typeToFile(this.type))
45
51
  } else {
46
52
  fncToCall = this.workers
@@ -1,3 +1,6 @@
1
+ // when thread starts, make sure it is recorded
2
+ const env = require("../environment")
3
+ env.setInThread()
1
4
  const ScriptRunner = require("../utilities/scriptRunner")
2
5
  const { integrations } = require("../integrations")
3
6