@budibase/server 0.9.185-alpha.14 → 0.9.185-alpha.18

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 (188) hide show
  1. package/Dockerfile +7 -1
  2. package/__mocks__/oracledb.ts +31 -0
  3. package/builder/assets/{index.8ed163fd.js → index.21479aff.js} +152 -152
  4. package/builder/assets/{index.4a7c5636.css → index.67ac3a72.css} +1 -1
  5. package/builder/assets/oracle.66592424.png +0 -0
  6. package/builder/index.html +2 -2
  7. package/coverage/clover.xml +84 -84
  8. package/coverage/coverage-final.json +4 -4
  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 +4 -4
  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 +1 -1
  37. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  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 +1 -1
  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 +1 -1
  146. package/coverage/lcov-report/threads/index.html +1 -1
  147. package/coverage/lcov-report/threads/index.js.html +1 -1
  148. package/coverage/lcov-report/threads/query.js.html +1 -1
  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 +83 -83
  169. package/dist/definitions/datasource.js +1 -0
  170. package/dist/integrations/base/sql.js +4 -0
  171. package/dist/integrations/index.js +6 -0
  172. package/dist/integrations/oracle.js +345 -0
  173. package/dist/integrations/utils.js +10 -1
  174. package/dist/package.json +8 -4
  175. package/dist/tsconfig.tsbuildinfo +1 -1
  176. package/package.json +9 -5
  177. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  178. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  179. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  180. package/scripts/integrations/oracle/oracle.md +90 -0
  181. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  182. package/src/definitions/datasource.ts +1 -0
  183. package/src/integrations/base/IntegrationBase.ts +4 -4
  184. package/src/integrations/base/sql.ts +3 -0
  185. package/src/integrations/index.ts +7 -0
  186. package/src/integrations/oracle.ts +445 -0
  187. package/src/integrations/tests/oracle.spec.js +94 -0
  188. package/src/integrations/utils.ts +10 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "0.9.185-alpha.14",
4
+ "version": "0.9.185-alpha.18",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.js",
7
7
  "repository": {
@@ -68,9 +68,9 @@
68
68
  "author": "Budibase",
69
69
  "license": "AGPL-3.0-or-later",
70
70
  "dependencies": {
71
- "@budibase/auth": "^0.9.185-alpha.14",
72
- "@budibase/client": "^0.9.185-alpha.14",
73
- "@budibase/string-templates": "^0.9.185-alpha.14",
71
+ "@budibase/auth": "^0.9.185-alpha.18",
72
+ "@budibase/client": "^0.9.185-alpha.18",
73
+ "@budibase/string-templates": "^0.9.185-alpha.18",
74
74
  "@bull-board/api": "^3.7.0",
75
75
  "@bull-board/koa": "^3.7.0",
76
76
  "@elastic/elasticsearch": "7.10.0",
@@ -135,6 +135,7 @@
135
135
  "@types/koa": "^2.13.3",
136
136
  "@types/koa-router": "^7.4.2",
137
137
  "@types/node": "^15.12.4",
138
+ "@types/oracledb": "^5.2.1",
138
139
  "@typescript-eslint/parser": "4.28.0",
139
140
  "babel-jest": "^27.0.2",
140
141
  "copyfiles": "^2.4.1",
@@ -150,5 +151,8 @@
150
151
  "typescript": "^4.3.5",
151
152
  "update-dotenv": "^1.1.1"
152
153
  },
153
- "gitHead": "4e76951a03d3ea6793c1820d6c5590a36de6496e"
154
+ "optionalDependencies": {
155
+ "oracledb": "^5.3.0"
156
+ },
157
+ "gitHead": "aefc0a46953807c460bfb6ab11c5e541ef17a741"
154
158
  }
@@ -0,0 +1,19 @@
1
+ # For more information see:
2
+ # https://container-registry.oracle.com/
3
+ # - Database > Express
4
+ version: "3.8"
5
+ services:
6
+ db:
7
+ container_name: oracle-xe
8
+ platform: linux/x86_64
9
+ image: container-registry.oracle.com/database/express:18.4.0-xe
10
+ environment:
11
+ ORACLE_PWD: oracle
12
+ ports:
13
+ - 1521:1521
14
+ - 5500:5500
15
+ volumes:
16
+ - oracle_data:/opt/oracle/oradata
17
+
18
+ volumes:
19
+ oracle_data:
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ # Must be root to continue
4
+ if [[ $(id -u) -ne 0 ]] ; then echo "Please run as root" ; exit 1 ; fi
5
+
6
+ # Allow for re-runs
7
+ rm -rf /opt/oracle
8
+
9
+ echo "Installing oracle instant client"
10
+
11
+ # copy and unzip package
12
+ mkdir -p /opt/oracle
13
+ cp scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip /opt/oracle
14
+ cd /opt/oracle
15
+ unzip -qq basiclite-21.4.zip -d .
16
+ rm *.zip
17
+ mv instantclient* instantclient
18
+
19
+ # update runtime link path
20
+ sh -c "echo /opt/oracle/instantclient > /etc/ld.so.conf.d/oracle-instantclient.conf"
21
+ ldconfig /etc/ld.so.conf.d
22
+
23
+ echo "Installation complete"
@@ -0,0 +1,90 @@
1
+ # Installation
2
+
3
+ ## Database
4
+
5
+ **Important**
6
+ - Oracle database is supported only on **x86-64 architecture**
7
+ - Oracle database is **not supported on Mac ARM architecture** (either via docker or linux virtualization)
8
+
9
+ To install oracle express edition simply run `docker-compose up`
10
+
11
+ - A single instance pluggable database (PDB) will be created named `xepdb`
12
+ - The default password is configured in the compose file as `oracle`
13
+ - The `system`, `sys` and `pdbadmin` users all share this password
14
+
15
+ ## Instant Client
16
+
17
+ Before oracle can be connected to from nodejs, the oracle client must be installed.
18
+ For more information see https://www.oracle.com/database/technologies/instant-client/downloads.html
19
+
20
+ **Important**
21
+ - Oracle client is supported only on **x86-64 architecture**
22
+ - Oracle client is **not supported on Mac ARM architecture**
23
+
24
+ ### Linux
25
+ Run the provided install script for linux from the `server` root path:
26
+
27
+ ```bash
28
+ sudo /bin/bash -e scripts/integrations/oracle/instantclient/linux/x86-64/install.sh
29
+ ```
30
+ For more information see: https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html#ic_x64_inst
31
+
32
+ ### Mac
33
+ **This has not yet been tested**
34
+
35
+ See: https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html#ic_osx_inst
36
+
37
+ # Management
38
+ To connect to oracle sql command line:
39
+
40
+ ```bash
41
+ docker exec -it oracle-xe sqlplus -l system/oracle@localhost/xepdb1
42
+ ```
43
+
44
+ To create a new schema (where a user is the same as a schema in oracle) named `sales`:
45
+
46
+ ```sql
47
+ define USERNAME = sales
48
+
49
+ create user &USERNAME;
50
+
51
+ alter user &USERNAME
52
+ default tablespace users
53
+ temporary tablespace temp
54
+ quota unlimited on users;
55
+
56
+ grant create session,
57
+ create view,
58
+ create sequence,
59
+ create procedure,
60
+ create table,
61
+ create trigger,
62
+ create type,
63
+ create materialized view
64
+ to &USERNAME;
65
+ ```
66
+
67
+ To set the password for the sales schema use:
68
+
69
+ ```sql
70
+ define USERNAME = sales
71
+ define PASSWORD = sales
72
+
73
+ alter user &USERNAME identified by &PASSWORD;
74
+ ```
75
+
76
+ As before the database schema can now be connected to using:
77
+ ```bash
78
+ docker exec -it oracle-xe sqlplus -l sales/sales@localhost:1521/xepdb1
79
+ ```
80
+
81
+ ## HR Schema
82
+
83
+ The `HR` schema is populated with dummy data by default in oracle for testing purposes.
84
+ To connect to the HR schema first update the user password and unlock the account by performing
85
+ ```sql
86
+ ALTER USER hr ACCOUNT UNLOCK;
87
+ ALTER USER hr IDENTIFIED BY hr
88
+ ```
89
+ You should now be able to connect to the hr schema using the credentials hr/hr
90
+
@@ -11,7 +11,7 @@ services:
11
11
  ports:
12
12
  - "5432:5432"
13
13
  volumes:
14
- #- pg_data:/var/lib/postgresql/data/
14
+ - pg_data:/var/lib/postgresql/data/
15
15
  - ./init.sql:/docker-entrypoint-initdb.d/init.sql
16
16
 
17
17
  pgadmin:
@@ -24,5 +24,5 @@ services:
24
24
  ports:
25
25
  - "5050:80"
26
26
 
27
- #volumes:
28
- # pg_data:
27
+ volumes:
28
+ pg_data:
@@ -46,6 +46,7 @@ export enum SourceNames {
46
46
  MYSQL = "MYSQL",
47
47
  ARANGODB = "ARANGODB",
48
48
  REST = "REST",
49
+ ORACLE = "ORACLE",
49
50
  }
50
51
 
51
52
  export enum IncludeRelationships {
@@ -1,6 +1,6 @@
1
1
  export interface IntegrationBase {
2
- create?(query: any): Promise<[any]>
3
- read?(query: any): Promise<[any]>
4
- update?(query: any): Promise<[any]>
5
- delete?(query: any): Promise<[any]>
2
+ create?(query: any): Promise<any[]>
3
+ read?(query: any): Promise<any[]>
4
+ update?(query: any): Promise<any[]>
5
+ delete?(query: any): Promise<any[]>
6
6
  }
@@ -416,6 +416,8 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
416
416
  id = results?.[0].id
417
417
  } else if (sqlClient === SqlClients.MY_SQL) {
418
418
  id = results?.insertId
419
+ } else if (sqlClient === SqlClients.ORACLE) {
420
+ id = response.outBinds[0][0]
419
421
  }
420
422
  row = processFn(
421
423
  await this.getReturningRow(queryFn, this.checkLookupKeys(id, json))
@@ -428,4 +430,5 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
428
430
  }
429
431
  }
430
432
 
433
+ export default SqlQueryBuilder
431
434
  module.exports = SqlQueryBuilder
@@ -39,6 +39,13 @@ const INTEGRATIONS = {
39
39
  [SourceNames.REST]: rest.integration,
40
40
  }
41
41
 
42
+ // optionally add oracle integration if the oracle binary can be installed
43
+ if (!(process.arch === 'arm64' && process.platform === 'darwin')) {
44
+ const oracle = require("./oracle")
45
+ DEFINITIONS[SourceNames.ORACLE] = oracle.schema
46
+ INTEGRATIONS[SourceNames.ORACLE] = oracle.integration
47
+ }
48
+
42
49
  module.exports = {
43
50
  definitions: DEFINITIONS,
44
51
  integrations: INTEGRATIONS,
@@ -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
+ }