Merge pull request #3301 from Budibase/oracle-datasource
Oracle datasource
This commit is contained in:
commit
dd9226aeeb
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
export let width = "18"
|
||||||
|
export let height = "18"
|
||||||
|
|
||||||
|
import OracleLogo from "assets/oracle.png"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class>
|
||||||
|
<img {height} {width} src={OracleLogo} alt="oracle logo" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
padding-top: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -10,6 +10,7 @@ import MySQL from "./MySQL.svelte"
|
||||||
import ArangoDB from "./ArangoDB.svelte"
|
import ArangoDB from "./ArangoDB.svelte"
|
||||||
import Rest from "./Rest.svelte"
|
import Rest from "./Rest.svelte"
|
||||||
import Budibase from "./Budibase.svelte"
|
import Budibase from "./Budibase.svelte"
|
||||||
|
import Oracle from "./Oracle.svelte"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
BUDIBASE: Budibase,
|
BUDIBASE: Budibase,
|
||||||
|
@ -24,4 +25,5 @@ export default {
|
||||||
MYSQL: MySQL,
|
MYSQL: MySQL,
|
||||||
ARANGODB: ArangoDB,
|
ARANGODB: ArangoDB,
|
||||||
REST: Rest,
|
REST: Rest,
|
||||||
|
ORACLE: Oracle,
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ export const IntegrationNames = {
|
||||||
SQL_SERVER: "SQL Server",
|
SQL_SERVER: "SQL Server",
|
||||||
AIRTABLE: "Airtable",
|
AIRTABLE: "Airtable",
|
||||||
ARANGODB: "ArangoDB",
|
ARANGODB: "ArangoDB",
|
||||||
|
ORACLE: "Oracle",
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields on the user table that cannot be edited
|
// fields on the user table that cannot be edited
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/bbui@^0.9.139", "@budibase/bbui@^0.9.185-alpha.9", "@budibase/bbui@^0.9.187":
|
"@budibase/bbui@^0.9.139":
|
||||||
version "0.9.187"
|
version "0.9.187"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.187.tgz#84f0a37301cfa41f50eaa335243ac08923d9e34f"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.187.tgz#84f0a37301cfa41f50eaa335243ac08923d9e34f"
|
||||||
integrity sha512-Qy24x99NloRAoG78NMdzoJuX3Gbf+eZdHeYTAeUchljB4o2W2L/Ous8qYBzqigYtVcChjzteSTAZ2jCLq458Vg==
|
integrity sha512-Qy24x99NloRAoG78NMdzoJuX3Gbf+eZdHeYTAeUchljB4o2W2L/Ous8qYBzqigYtVcChjzteSTAZ2jCLq458Vg==
|
||||||
|
@ -969,14 +969,63 @@
|
||||||
svelte-flatpickr "^3.2.3"
|
svelte-flatpickr "^3.2.3"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/client@^0.9.185-alpha.9":
|
"@budibase/bbui@^0.9.185-alpha.12", "@budibase/bbui@^0.9.188":
|
||||||
version "0.9.187"
|
version "0.9.188"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.187.tgz#8ab8a8edc81e65dc5bac15bcbc5058666d0a2c64"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.188.tgz#82c108172fbf81a84378e0ef4ca7cba61ea8d0ba"
|
||||||
integrity sha512-eSNikZWkYxqy0d0zGBY6m8lDlVMNDhr7DFlqyFZOha03Abu1lS+YGKRJUb0TSkx7y4Qmb4hmvU0wj074ToxPJQ==
|
integrity sha512-KevJxHdASITX9RzLvm+b2K3VMwqYFTumvrlpStAP6UIoyPkls0xaAc2KiJJ7Kkq48UkkBtAbOYaMxsFbAaTsbQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/bbui" "^0.9.187"
|
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
||||||
|
"@spectrum-css/actionbutton" "^1.0.1"
|
||||||
|
"@spectrum-css/actiongroup" "^1.0.1"
|
||||||
|
"@spectrum-css/avatar" "^3.0.2"
|
||||||
|
"@spectrum-css/button" "^3.0.1"
|
||||||
|
"@spectrum-css/buttongroup" "^3.0.2"
|
||||||
|
"@spectrum-css/checkbox" "^3.0.2"
|
||||||
|
"@spectrum-css/dialog" "^3.0.1"
|
||||||
|
"@spectrum-css/divider" "^1.0.3"
|
||||||
|
"@spectrum-css/dropzone" "^3.0.2"
|
||||||
|
"@spectrum-css/fieldgroup" "^3.0.2"
|
||||||
|
"@spectrum-css/fieldlabel" "^3.0.1"
|
||||||
|
"@spectrum-css/icon" "^3.0.1"
|
||||||
|
"@spectrum-css/illustratedmessage" "^3.0.2"
|
||||||
|
"@spectrum-css/inputgroup" "^3.0.2"
|
||||||
|
"@spectrum-css/label" "^2.0.10"
|
||||||
|
"@spectrum-css/link" "^3.1.1"
|
||||||
|
"@spectrum-css/menu" "^3.0.1"
|
||||||
|
"@spectrum-css/modal" "^3.0.1"
|
||||||
|
"@spectrum-css/pagination" "^3.0.3"
|
||||||
|
"@spectrum-css/picker" "^1.0.1"
|
||||||
|
"@spectrum-css/popover" "^3.0.1"
|
||||||
|
"@spectrum-css/progressbar" "^1.0.2"
|
||||||
|
"@spectrum-css/progresscircle" "^1.0.2"
|
||||||
|
"@spectrum-css/radio" "^3.0.2"
|
||||||
|
"@spectrum-css/search" "^3.0.2"
|
||||||
|
"@spectrum-css/sidenav" "^3.0.2"
|
||||||
|
"@spectrum-css/statuslight" "^3.0.2"
|
||||||
|
"@spectrum-css/stepper" "^3.0.3"
|
||||||
|
"@spectrum-css/switch" "^1.0.2"
|
||||||
|
"@spectrum-css/table" "^3.0.1"
|
||||||
|
"@spectrum-css/tabs" "^3.0.1"
|
||||||
|
"@spectrum-css/tags" "^3.0.2"
|
||||||
|
"@spectrum-css/textfield" "^3.0.1"
|
||||||
|
"@spectrum-css/toast" "^3.0.1"
|
||||||
|
"@spectrum-css/tooltip" "^3.0.3"
|
||||||
|
"@spectrum-css/treeview" "^3.0.2"
|
||||||
|
"@spectrum-css/typography" "^3.0.1"
|
||||||
|
"@spectrum-css/underlay" "^2.0.9"
|
||||||
|
"@spectrum-css/vars" "^3.0.1"
|
||||||
|
dayjs "^1.10.4"
|
||||||
|
svelte-flatpickr "^3.2.3"
|
||||||
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
|
"@budibase/client@^0.9.185-alpha.12":
|
||||||
|
version "0.9.188"
|
||||||
|
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.188.tgz#c5bf6f3bccdb370b236b9e69e0118334ad3eccfd"
|
||||||
|
integrity sha512-yP2WLWb2yQAwPBxVzpNGjSHpATMZMzcxl2gK6vw662F7YC8xGFHNfZZqEwPvrVwnx+d7LFZR/kJxJOvvk7YCVw==
|
||||||
|
dependencies:
|
||||||
|
"@budibase/bbui" "^0.9.188"
|
||||||
"@budibase/standard-components" "^0.9.139"
|
"@budibase/standard-components" "^0.9.139"
|
||||||
"@budibase/string-templates" "^0.9.187"
|
"@budibase/string-templates" "^0.9.188"
|
||||||
regexparam "^1.3.0"
|
regexparam "^1.3.0"
|
||||||
shortid "^2.2.15"
|
shortid "^2.2.15"
|
||||||
svelte-spa-router "^3.0.5"
|
svelte-spa-router "^3.0.5"
|
||||||
|
@ -1031,10 +1080,10 @@
|
||||||
svelte-apexcharts "^1.0.2"
|
svelte-apexcharts "^1.0.2"
|
||||||
svelte-flatpickr "^3.1.0"
|
svelte-flatpickr "^3.1.0"
|
||||||
|
|
||||||
"@budibase/string-templates@^0.9.185-alpha.9", "@budibase/string-templates@^0.9.187":
|
"@budibase/string-templates@^0.9.185-alpha.12", "@budibase/string-templates@^0.9.188":
|
||||||
version "0.9.187"
|
version "0.9.188"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.187.tgz#e6c7b3b3e9c93014731b0b4e01f482fad6d2b12f"
|
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.188.tgz#f37836ed23dbd2217cb157030ada7cd59f6a2165"
|
||||||
integrity sha512-230yCwmKv6gG0Bi5xUteAKJN7BgYpwepJFEqmF8TrxDaV+qJO55H9br918FbTQ9W9g9Vv0HKU0xNdSfLTrlqhQ==
|
integrity sha512-O/bL0I5OJO9W2OizIe9vBHowCLwwASPBrsGiAIB8L0x6AivYMq8j1mvNRwLXZjpHTjv86bU/LyG/3CP837oDsg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/handlebars-helpers" "^0.11.7"
|
"@budibase/handlebars-helpers" "^0.11.7"
|
||||||
dayjs "^1.10.4"
|
dayjs "^1.10.4"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,6 @@
|
||||||
FROM node:14-alpine
|
FROM node:14-slim
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
LABEL com.centurylinklabs.watchtower.lifecycle.pre-check="scripts/watchtower-hooks/pre-check.sh"
|
LABEL com.centurylinklabs.watchtower.lifecycle.pre-check="scripts/watchtower-hooks/pre-check.sh"
|
||||||
LABEL com.centurylinklabs.watchtower.lifecycle.pre-update="scripts/watchtower-hooks/pre-update.sh"
|
LABEL com.centurylinklabs.watchtower.lifecycle.pre-update="scripts/watchtower-hooks/pre-update.sh"
|
||||||
|
@ -16,6 +18,10 @@ COPY . ./
|
||||||
RUN yarn
|
RUN yarn
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
|
# Install client for oracle datasource
|
||||||
|
RUN apt-get install unzip libaio1
|
||||||
|
RUN /bin/bash -e scripts/integrations/oracle/instantclient/linux/x86-64/install.sh
|
||||||
|
|
||||||
EXPOSE 4001
|
EXPOSE 4001
|
||||||
|
|
||||||
# have to add node environment production after install
|
# have to add node environment production after install
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
module OracleDbMock {
|
||||||
|
// mock execute
|
||||||
|
const execute = jest.fn(() => ({
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
a: "string",
|
||||||
|
b: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
|
||||||
|
const close = jest.fn()
|
||||||
|
|
||||||
|
// mock connection
|
||||||
|
function Connection() {}
|
||||||
|
Connection.prototype.execute = execute
|
||||||
|
Connection.prototype.close = close
|
||||||
|
|
||||||
|
// mock oracledb
|
||||||
|
const oracleDb: any = {}
|
||||||
|
oracleDb.getConnection = jest.fn(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
return new Connection()
|
||||||
|
})
|
||||||
|
|
||||||
|
// expose mocks
|
||||||
|
oracleDb.executeMock = execute
|
||||||
|
oracleDb.closeMock = close
|
||||||
|
|
||||||
|
module.exports = oracleDb
|
||||||
|
}
|
|
@ -135,6 +135,7 @@
|
||||||
"@types/koa": "^2.13.3",
|
"@types/koa": "^2.13.3",
|
||||||
"@types/koa-router": "^7.4.2",
|
"@types/koa-router": "^7.4.2",
|
||||||
"@types/node": "^15.12.4",
|
"@types/node": "^15.12.4",
|
||||||
|
"@types/oracledb": "^5.2.1",
|
||||||
"@typescript-eslint/parser": "4.28.0",
|
"@typescript-eslint/parser": "4.28.0",
|
||||||
"babel-jest": "^27.0.2",
|
"babel-jest": "^27.0.2",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
|
@ -150,5 +151,8 @@
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^4.3.5",
|
||||||
"update-dotenv": "^1.1.1"
|
"update-dotenv": "^1.1.1"
|
||||||
},
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"oracledb": "^5.3.0"
|
||||||
|
},
|
||||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
|
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
|
||||||
}
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
# For more information see:
|
||||||
|
# https://container-registry.oracle.com/
|
||||||
|
# - Database > Express
|
||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
container_name: oracle-xe
|
||||||
|
platform: linux/x86_64
|
||||||
|
image: container-registry.oracle.com/database/express:18.4.0-xe
|
||||||
|
environment:
|
||||||
|
ORACLE_PWD: oracle
|
||||||
|
ports:
|
||||||
|
- 1521:1521
|
||||||
|
- 5500:5500
|
||||||
|
volumes:
|
||||||
|
- oracle_data:/opt/oracle/oradata
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
oracle_data:
|
Binary file not shown.
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Must be root to continue
|
||||||
|
if [[ $(id -u) -ne 0 ]] ; then echo "Please run as root" ; exit 1 ; fi
|
||||||
|
|
||||||
|
# Allow for re-runs
|
||||||
|
rm -rf /opt/oracle
|
||||||
|
|
||||||
|
echo "Installing oracle instant client"
|
||||||
|
|
||||||
|
# copy and unzip package
|
||||||
|
mkdir -p /opt/oracle
|
||||||
|
cp scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip /opt/oracle
|
||||||
|
cd /opt/oracle
|
||||||
|
unzip -qq basiclite-21.4.zip -d .
|
||||||
|
rm *.zip
|
||||||
|
mv instantclient* instantclient
|
||||||
|
|
||||||
|
# update runtime link path
|
||||||
|
sh -c "echo /opt/oracle/instantclient > /etc/ld.so.conf.d/oracle-instantclient.conf"
|
||||||
|
ldconfig /etc/ld.so.conf.d
|
||||||
|
|
||||||
|
echo "Installation complete"
|
|
@ -0,0 +1,90 @@
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
## Database
|
||||||
|
|
||||||
|
**Important**
|
||||||
|
- Oracle database is supported only on **x86-64 architecture**
|
||||||
|
- Oracle database is **not supported on Mac ARM architecture** (either via docker or linux virtualization)
|
||||||
|
|
||||||
|
To install oracle express edition simply run `docker-compose up`
|
||||||
|
|
||||||
|
- A single instance pluggable database (PDB) will be created named `xepdb`
|
||||||
|
- The default password is configured in the compose file as `oracle`
|
||||||
|
- The `system`, `sys` and `pdbadmin` users all share this password
|
||||||
|
|
||||||
|
## Instant Client
|
||||||
|
|
||||||
|
Before oracle can be connected to from nodejs, the oracle client must be installed.
|
||||||
|
For more information see https://www.oracle.com/database/technologies/instant-client/downloads.html
|
||||||
|
|
||||||
|
**Important**
|
||||||
|
- Oracle client is supported only on **x86-64 architecture**
|
||||||
|
- Oracle client is **not supported on Mac ARM architecture**
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
Run the provided install script for linux from the `server` root path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo /bin/bash -e scripts/integrations/oracle/instantclient/linux/x86-64/install.sh
|
||||||
|
```
|
||||||
|
For more information see: https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html#ic_x64_inst
|
||||||
|
|
||||||
|
### Mac
|
||||||
|
**This has not yet been tested**
|
||||||
|
|
||||||
|
See: https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html#ic_osx_inst
|
||||||
|
|
||||||
|
# Management
|
||||||
|
To connect to oracle sql command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it oracle-xe sqlplus -l system/oracle@localhost/xepdb1
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a new schema (where a user is the same as a schema in oracle) named `sales`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
define USERNAME = sales
|
||||||
|
|
||||||
|
create user &USERNAME;
|
||||||
|
|
||||||
|
alter user &USERNAME
|
||||||
|
default tablespace users
|
||||||
|
temporary tablespace temp
|
||||||
|
quota unlimited on users;
|
||||||
|
|
||||||
|
grant create session,
|
||||||
|
create view,
|
||||||
|
create sequence,
|
||||||
|
create procedure,
|
||||||
|
create table,
|
||||||
|
create trigger,
|
||||||
|
create type,
|
||||||
|
create materialized view
|
||||||
|
to &USERNAME;
|
||||||
|
```
|
||||||
|
|
||||||
|
To set the password for the sales schema use:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
define USERNAME = sales
|
||||||
|
define PASSWORD = sales
|
||||||
|
|
||||||
|
alter user &USERNAME identified by &PASSWORD;
|
||||||
|
```
|
||||||
|
|
||||||
|
As before the database schema can now be connected to using:
|
||||||
|
```bash
|
||||||
|
docker exec -it oracle-xe sqlplus -l sales/sales@localhost:1521/xepdb1
|
||||||
|
```
|
||||||
|
|
||||||
|
## HR Schema
|
||||||
|
|
||||||
|
The `HR` schema is populated with dummy data by default in oracle for testing purposes.
|
||||||
|
To connect to the HR schema first update the user password and unlock the account by performing
|
||||||
|
```sql
|
||||||
|
ALTER USER hr ACCOUNT UNLOCK;
|
||||||
|
ALTER USER hr IDENTIFIED BY hr
|
||||||
|
```
|
||||||
|
You should now be able to connect to the hr schema using the credentials hr/hr
|
||||||
|
|
|
@ -11,7 +11,7 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
#- pg_data:/var/lib/postgresql/data/
|
- pg_data:/var/lib/postgresql/data/
|
||||||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
|
|
||||||
pgadmin:
|
pgadmin:
|
||||||
|
@ -24,5 +24,5 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "5050:80"
|
- "5050:80"
|
||||||
|
|
||||||
#volumes:
|
volumes:
|
||||||
# pg_data:
|
pg_data:
|
||||||
|
|
|
@ -46,6 +46,7 @@ export enum SourceNames {
|
||||||
MYSQL = "MYSQL",
|
MYSQL = "MYSQL",
|
||||||
ARANGODB = "ARANGODB",
|
ARANGODB = "ARANGODB",
|
||||||
REST = "REST",
|
REST = "REST",
|
||||||
|
ORACLE = "ORACLE",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum IncludeRelationships {
|
export enum IncludeRelationships {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export interface IntegrationBase {
|
export interface IntegrationBase {
|
||||||
create?(query: any): Promise<[any]>
|
create?(query: any): Promise<any[]>
|
||||||
read?(query: any): Promise<[any]>
|
read?(query: any): Promise<any[]>
|
||||||
update?(query: any): Promise<[any]>
|
update?(query: any): Promise<any[]>
|
||||||
delete?(query: any): Promise<[any]>
|
delete?(query: any): Promise<any[]>
|
||||||
}
|
}
|
||||||
|
|
|
@ -416,6 +416,8 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
||||||
id = results?.[0].id
|
id = results?.[0].id
|
||||||
} else if (sqlClient === SqlClients.MY_SQL) {
|
} else if (sqlClient === SqlClients.MY_SQL) {
|
||||||
id = results?.insertId
|
id = results?.insertId
|
||||||
|
} else if (sqlClient === SqlClients.ORACLE) {
|
||||||
|
id = response.outBinds[0][0]
|
||||||
}
|
}
|
||||||
row = processFn(
|
row = processFn(
|
||||||
await this.getReturningRow(queryFn, this.checkLookupKeys(id, json))
|
await this.getReturningRow(queryFn, this.checkLookupKeys(id, json))
|
||||||
|
@ -428,4 +430,5 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default SqlQueryBuilder
|
||||||
module.exports = SqlQueryBuilder
|
module.exports = SqlQueryBuilder
|
||||||
|
|
|
@ -39,6 +39,13 @@ const INTEGRATIONS = {
|
||||||
[SourceNames.REST]: rest.integration,
|
[SourceNames.REST]: rest.integration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optionally add oracle integration if the oracle binary can be installed
|
||||||
|
if (!(process.arch === 'arm64' && process.platform === 'darwin')) {
|
||||||
|
const oracle = require("./oracle")
|
||||||
|
DEFINITIONS[SourceNames.ORACLE] = oracle.schema
|
||||||
|
INTEGRATIONS[SourceNames.ORACLE] = oracle.integration
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
definitions: DEFINITIONS,
|
definitions: DEFINITIONS,
|
||||||
integrations: INTEGRATIONS,
|
integrations: INTEGRATIONS,
|
||||||
|
|
|
@ -0,0 +1,445 @@
|
||||||
|
import {
|
||||||
|
Integration,
|
||||||
|
DatasourceFieldTypes,
|
||||||
|
QueryTypes,
|
||||||
|
SqlQuery,
|
||||||
|
QueryJson,
|
||||||
|
Operation,
|
||||||
|
} from "../definitions/datasource"
|
||||||
|
import {
|
||||||
|
finaliseExternalTables,
|
||||||
|
getSqlQuery,
|
||||||
|
buildExternalTableId,
|
||||||
|
convertSqlType,
|
||||||
|
SqlClients,
|
||||||
|
} from "./utils"
|
||||||
|
import oracledb, {
|
||||||
|
ExecuteOptions,
|
||||||
|
Result,
|
||||||
|
Connection,
|
||||||
|
ConnectionAttributes,
|
||||||
|
BindParameters,
|
||||||
|
} from "oracledb"
|
||||||
|
import Sql from "./base/sql"
|
||||||
|
import { Table } from "../definitions/common"
|
||||||
|
import { DatasourcePlus } from "./base/datasourcePlus"
|
||||||
|
import { FieldTypes } from "../constants"
|
||||||
|
|
||||||
|
module OracleModule {
|
||||||
|
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT
|
||||||
|
|
||||||
|
interface OracleConfig {
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
database: string
|
||||||
|
user: string
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const SCHEMA: Integration = {
|
||||||
|
docs: "https://github.com/oracle/node-oracledb",
|
||||||
|
plus: true,
|
||||||
|
friendlyName: "Oracle",
|
||||||
|
description:
|
||||||
|
"Oracle Database is an object-relational database management system developed by Oracle Corporation",
|
||||||
|
datasource: {
|
||||||
|
host: {
|
||||||
|
type: DatasourceFieldTypes.STRING,
|
||||||
|
default: "localhost",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
port: {
|
||||||
|
type: DatasourceFieldTypes.NUMBER,
|
||||||
|
required: true,
|
||||||
|
default: 1521,
|
||||||
|
},
|
||||||
|
database: {
|
||||||
|
type: DatasourceFieldTypes.STRING,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: DatasourceFieldTypes.STRING,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: DatasourceFieldTypes.PASSWORD,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
create: {
|
||||||
|
type: QueryTypes.SQL,
|
||||||
|
},
|
||||||
|
read: {
|
||||||
|
type: QueryTypes.SQL,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
type: QueryTypes.SQL,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
type: QueryTypes.SQL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNSUPPORTED_TYPES = ["BLOB", "CLOB", "NCLOB"]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw query response
|
||||||
|
*/
|
||||||
|
interface ColumnsResponse {
|
||||||
|
TABLE_NAME: string
|
||||||
|
COLUMN_NAME: string
|
||||||
|
DATA_TYPE: string
|
||||||
|
DATA_DEFAULT: string | null
|
||||||
|
COLUMN_ID: number
|
||||||
|
CONSTRAINT_NAME: string | null
|
||||||
|
CONSTRAINT_TYPE: string | null
|
||||||
|
R_CONSTRAINT_NAME: string | null
|
||||||
|
SEARCH_CONDITION: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An oracle constraint
|
||||||
|
*/
|
||||||
|
interface OracleConstraint {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
relatedConstraintName: string | null
|
||||||
|
searchCondition: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An oracle column and it's related constraints
|
||||||
|
*/
|
||||||
|
interface OracleColumn {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
default: string | null
|
||||||
|
id: number
|
||||||
|
constraints: { [key: string]: OracleConstraint }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An oracle table and it's related columns
|
||||||
|
*/
|
||||||
|
interface OracleTable {
|
||||||
|
name: string
|
||||||
|
columns: { [key: string]: OracleColumn }
|
||||||
|
}
|
||||||
|
|
||||||
|
const OracleContraintTypes = {
|
||||||
|
PRIMARY: "P",
|
||||||
|
NOT_NULL_OR_CHECK: "C",
|
||||||
|
FOREIGN_KEY: "R",
|
||||||
|
UNIQUE: "U",
|
||||||
|
}
|
||||||
|
|
||||||
|
class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
|
private readonly config: OracleConfig
|
||||||
|
|
||||||
|
public tables: Record<string, Table> = {}
|
||||||
|
public schemaErrors: Record<string, string> = {}
|
||||||
|
|
||||||
|
private readonly COLUMNS_SQL = `
|
||||||
|
SELECT
|
||||||
|
tabs.table_name,
|
||||||
|
cols.column_name,
|
||||||
|
cols.data_type,
|
||||||
|
cols.data_default,
|
||||||
|
cols.column_id,
|
||||||
|
cons.constraint_name,
|
||||||
|
cons.constraint_type,
|
||||||
|
cons.r_constraint_name,
|
||||||
|
cons.search_condition
|
||||||
|
FROM
|
||||||
|
user_tables tabs
|
||||||
|
JOIN
|
||||||
|
user_tab_columns cols
|
||||||
|
ON tabs.table_name = cols.table_name
|
||||||
|
LEFT JOIN
|
||||||
|
user_cons_columns col_cons
|
||||||
|
ON cols.column_name = col_cons.column_name
|
||||||
|
AND cols.table_name = col_cons.table_name
|
||||||
|
LEFT JOIN
|
||||||
|
user_constraints cons
|
||||||
|
ON col_cons.constraint_name = cons.constraint_name
|
||||||
|
AND cons.table_name = cols.table_name
|
||||||
|
WHERE
|
||||||
|
(cons.status = 'ENABLED'
|
||||||
|
OR cons.status IS NULL)
|
||||||
|
`
|
||||||
|
constructor(config: OracleConfig) {
|
||||||
|
super(SqlClients.ORACLE)
|
||||||
|
this.config = config
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the flat tabular columns and constraints data into a nested object
|
||||||
|
*/
|
||||||
|
private mapColumns(result: Result<ColumnsResponse>): {
|
||||||
|
[key: string]: OracleTable
|
||||||
|
} {
|
||||||
|
const oracleTables: { [key: string]: OracleTable } = {}
|
||||||
|
|
||||||
|
if (result.rows) {
|
||||||
|
result.rows.forEach(row => {
|
||||||
|
const tableName = row.TABLE_NAME
|
||||||
|
const columnName = row.COLUMN_NAME
|
||||||
|
const dataType = row.DATA_TYPE
|
||||||
|
const dataDefault = row.DATA_DEFAULT
|
||||||
|
const columnId = row.COLUMN_ID
|
||||||
|
const constraintName = row.CONSTRAINT_NAME
|
||||||
|
const constraintType = row.CONSTRAINT_TYPE
|
||||||
|
const relatedConstraintName = row.R_CONSTRAINT_NAME
|
||||||
|
const searchCondition = row.SEARCH_CONDITION
|
||||||
|
|
||||||
|
let table = oracleTables[tableName]
|
||||||
|
if (!table) {
|
||||||
|
table = {
|
||||||
|
name: tableName,
|
||||||
|
columns: {},
|
||||||
|
}
|
||||||
|
oracleTables[tableName] = table
|
||||||
|
}
|
||||||
|
|
||||||
|
let column = table.columns[columnName]
|
||||||
|
if (!column) {
|
||||||
|
column = {
|
||||||
|
name: columnName,
|
||||||
|
type: dataType,
|
||||||
|
default: dataDefault,
|
||||||
|
id: columnId,
|
||||||
|
constraints: {},
|
||||||
|
}
|
||||||
|
table.columns[columnName] = column
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constraintName && constraintType) {
|
||||||
|
let constraint = column.constraints[constraintName]
|
||||||
|
if (!constraint) {
|
||||||
|
constraint = {
|
||||||
|
name: constraintName,
|
||||||
|
type: constraintType,
|
||||||
|
relatedConstraintName: relatedConstraintName,
|
||||||
|
searchCondition: searchCondition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
column.constraints[constraintName] = constraint
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return oracleTables
|
||||||
|
}
|
||||||
|
|
||||||
|
private isSupportedColumn(column: OracleColumn) {
|
||||||
|
if (UNSUPPORTED_TYPES.includes(column.type)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private isAutoColumn(column: OracleColumn) {
|
||||||
|
if (column.default && column.default.toLowerCase().includes("nextval")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No native boolean in oracle. Best we can do is to check if a manual 1 or 0 number constraint has been set up
|
||||||
|
* This matches the default behaviour for generating DDL used in knex.
|
||||||
|
*/
|
||||||
|
private isBooleanType(column: OracleColumn): boolean {
|
||||||
|
if (
|
||||||
|
column.type.toLowerCase() === "number" &&
|
||||||
|
Object.values(column.constraints).filter(c => {
|
||||||
|
if (
|
||||||
|
c.type === OracleContraintTypes.NOT_NULL_OR_CHECK &&
|
||||||
|
c.searchCondition
|
||||||
|
) {
|
||||||
|
const condition = c.searchCondition
|
||||||
|
.replace(/\s/g, "") // remove spaces
|
||||||
|
.replace(/[']+/g, "") // remove quotes
|
||||||
|
if (
|
||||||
|
condition.includes("in(0,1)") ||
|
||||||
|
condition.includes("in(1,0)")
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}).length > 0
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private internalConvertType(column: OracleColumn): string {
|
||||||
|
if (this.isBooleanType(column)) {
|
||||||
|
return FieldTypes.BOOLEAN
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertSqlType(column.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the tables from the oracle table and assigns them to the datasource.
|
||||||
|
* @param {*} datasourceId - datasourceId to fetch
|
||||||
|
* @param entities - the tables that are to be built
|
||||||
|
*/
|
||||||
|
async buildSchema(datasourceId: string, entities: Record<string, Table>) {
|
||||||
|
const columnsResponse = await this.internalQuery<ColumnsResponse>({
|
||||||
|
sql: this.COLUMNS_SQL,
|
||||||
|
})
|
||||||
|
const oracleTables = this.mapColumns(columnsResponse)
|
||||||
|
|
||||||
|
const tables: { [key: string]: Table } = {}
|
||||||
|
|
||||||
|
// iterate each table
|
||||||
|
Object.values(oracleTables).forEach(oracleTable => {
|
||||||
|
let table = tables[oracleTable.name]
|
||||||
|
if (!table) {
|
||||||
|
table = {
|
||||||
|
_id: buildExternalTableId(datasourceId, oracleTable.name),
|
||||||
|
primary: [],
|
||||||
|
name: oracleTable.name,
|
||||||
|
schema: {},
|
||||||
|
}
|
||||||
|
tables[oracleTable.name] = table
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate each column on the table
|
||||||
|
Object.values(oracleTable.columns)
|
||||||
|
// remove columns that we can't read / save
|
||||||
|
.filter(oracleColumn => this.isSupportedColumn(oracleColumn))
|
||||||
|
// match the order of the columns in the db
|
||||||
|
.sort((c1, c2) => c1.id - c2.id)
|
||||||
|
.forEach(oracleColumn => {
|
||||||
|
const columnName = oracleColumn.name
|
||||||
|
let fieldSchema = table.schema[columnName]
|
||||||
|
if (!fieldSchema) {
|
||||||
|
fieldSchema = {
|
||||||
|
autocolumn: this.isAutoColumn(oracleColumn),
|
||||||
|
name: columnName,
|
||||||
|
type: this.internalConvertType(oracleColumn),
|
||||||
|
}
|
||||||
|
table.schema[columnName] = fieldSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate each constraint on the column
|
||||||
|
Object.values(oracleColumn.constraints).forEach(
|
||||||
|
oracleConstraint => {
|
||||||
|
if (oracleConstraint.type === OracleContraintTypes.PRIMARY) {
|
||||||
|
table.primary!.push(columnName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const final = finaliseExternalTables(tables, entities)
|
||||||
|
this.tables = final.tables
|
||||||
|
this.schemaErrors = final.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Knex default returning behaviour does not work with oracle
|
||||||
|
* Manually add the behaviour for the return column
|
||||||
|
*/
|
||||||
|
private addReturning(query: SqlQuery, bindings: BindParameters, returnColumn: string) {
|
||||||
|
if (bindings instanceof Array) {
|
||||||
|
bindings.push({ dir: oracledb.BIND_OUT })
|
||||||
|
query.sql = query.sql + ` returning \"${returnColumn}\" into :${bindings.length}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async internalQuery<T>(query: SqlQuery, returnColum?: string, operation?: string): Promise<Result<T>> {
|
||||||
|
let connection
|
||||||
|
try {
|
||||||
|
connection = await this.getConnection()
|
||||||
|
|
||||||
|
const options: ExecuteOptions = { autoCommit: true }
|
||||||
|
const bindings: BindParameters = query.bindings || []
|
||||||
|
|
||||||
|
if (returnColum && (operation === Operation.CREATE || operation === Operation.UPDATE)) {
|
||||||
|
this.addReturning(query, bindings, returnColum)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: Result<T> = await connection.execute<T>(
|
||||||
|
query.sql,
|
||||||
|
bindings,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
} finally {
|
||||||
|
if (connection) {
|
||||||
|
try {
|
||||||
|
await connection.close()
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getConnection = async (): Promise<Connection> => {
|
||||||
|
//connectString : "(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))(CONNECT_DATA =(SID= ORCL)))"
|
||||||
|
const connectString = `${this.config.host}:${this.config.port || 1521}/${
|
||||||
|
this.config.database
|
||||||
|
}`
|
||||||
|
const attributes: ConnectionAttributes = {
|
||||||
|
user: this.config.user,
|
||||||
|
password: this.config.user,
|
||||||
|
connectString,
|
||||||
|
}
|
||||||
|
return oracledb.getConnection(attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(query: SqlQuery | string): Promise<any[]> {
|
||||||
|
const response = await this.internalQuery<any>(getSqlQuery(query))
|
||||||
|
return response.rows && response.rows.length
|
||||||
|
? response.rows
|
||||||
|
: [{ created: true }]
|
||||||
|
}
|
||||||
|
|
||||||
|
async read(query: SqlQuery | string): Promise<any[]> {
|
||||||
|
const response = await this.internalQuery<any>(getSqlQuery(query))
|
||||||
|
return response.rows ? response.rows : []
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(query: SqlQuery | string): Promise<any[]> {
|
||||||
|
const response = await this.internalQuery(getSqlQuery(query))
|
||||||
|
return response.rows && response.rows.length
|
||||||
|
? response.rows
|
||||||
|
: [{ updated: true }]
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(query: SqlQuery | string): Promise<any[]> {
|
||||||
|
const response = await this.internalQuery(getSqlQuery(query))
|
||||||
|
return response.rows && response.rows.length
|
||||||
|
? response.rows
|
||||||
|
: [{ deleted: true }]
|
||||||
|
}
|
||||||
|
|
||||||
|
async query(json: QueryJson) {
|
||||||
|
const primaryKeys = json.meta!.table!.primary
|
||||||
|
const primaryKey = primaryKeys ? primaryKeys[0] : undefined
|
||||||
|
const queryFn = (query: any, operation: string) => this.internalQuery(query, primaryKey, operation)
|
||||||
|
const processFn = (response: any) => response.rows ? response.rows : []
|
||||||
|
const output = await this.queryWithReturning(json, queryFn, processFn)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
schema: SCHEMA,
|
||||||
|
integration: OracleIntegration,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
const oracledb = require("oracledb")
|
||||||
|
const OracleIntegration = require("../oracle")
|
||||||
|
jest.mock("oracledb")
|
||||||
|
|
||||||
|
class TestConfiguration {
|
||||||
|
constructor(config = {}) {
|
||||||
|
this.integration = new OracleIntegration.integration(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = { autoCommit: true }
|
||||||
|
|
||||||
|
describe("Oracle Integration", () => {
|
||||||
|
let config
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
config = new TestConfiguration()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
expect(oracledb.closeMock).toHaveBeenCalled()
|
||||||
|
expect(oracledb.closeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls the create method with the correct params", async () => {
|
||||||
|
const sql = "insert into users (name, age) values ('Joe', 123);"
|
||||||
|
await config.integration.create({
|
||||||
|
sql
|
||||||
|
})
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls the read method with the correct params", async () => {
|
||||||
|
const sql = "select * from users;"
|
||||||
|
await config.integration.read({
|
||||||
|
sql
|
||||||
|
})
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls the update method with the correct params", async () => {
|
||||||
|
const sql = "update table users set name = 'test';"
|
||||||
|
const response = await config.integration.update({
|
||||||
|
sql
|
||||||
|
})
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls the delete method with the correct params", async () => {
|
||||||
|
const sql = "delete from users where name = 'todelete';"
|
||||||
|
await config.integration.delete({
|
||||||
|
sql
|
||||||
|
})
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledWith(sql, [], options)
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("no rows returned", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
oracledb.executeMock.mockImplementation(() => ({ rows: [] }))
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the correct response when the create response has no rows", async () => {
|
||||||
|
const sql = "insert into users (name, age) values ('Joe', 123);"
|
||||||
|
const response = await config.integration.create({
|
||||||
|
sql
|
||||||
|
})
|
||||||
|
expect(response).toEqual([{ created: true }])
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the correct response when the update response has no rows", async () => {
|
||||||
|
const sql = "update table users set name = 'test';"
|
||||||
|
const response = await config.integration.update({
|
||||||
|
sql
|
||||||
|
})
|
||||||
|
expect(response).toEqual([{ updated: true }])
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the correct response when the delete response has no rows", async () => {
|
||||||
|
const sql = "delete from users where name = 'todelete';"
|
||||||
|
const response = await config.integration.delete({
|
||||||
|
sql
|
||||||
|
})
|
||||||
|
expect(response).toEqual([{ deleted: true }])
|
||||||
|
expect(oracledb.executeMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -32,12 +32,17 @@ const SQL_TYPE_MAP = {
|
||||||
fixed: FieldTypes.NUMBER,
|
fixed: FieldTypes.NUMBER,
|
||||||
datetime: FieldTypes.DATETIME,
|
datetime: FieldTypes.DATETIME,
|
||||||
tinyint: FieldTypes.BOOLEAN,
|
tinyint: FieldTypes.BOOLEAN,
|
||||||
|
long: FieldTypes.LONGFORM,
|
||||||
|
number: FieldTypes.NUMBER,
|
||||||
|
binary_float: FieldTypes.NUMBER,
|
||||||
|
binary_double: FieldTypes.NUMBER,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SqlClients {
|
export enum SqlClients {
|
||||||
MS_SQL = "mssql",
|
MS_SQL = "mssql",
|
||||||
POSTGRES = "pg",
|
POSTGRES = "pg",
|
||||||
MY_SQL = "mysql",
|
MY_SQL = "mysql",
|
||||||
|
ORACLE = "oracledb"
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isExternalTable(tableId: string) {
|
export function isExternalTable(tableId: string) {
|
||||||
|
@ -162,7 +167,7 @@ export function finaliseExternalTables(
|
||||||
entities: { [key: string]: any }
|
entities: { [key: string]: any }
|
||||||
) {
|
) {
|
||||||
const invalidColumns = Object.values(InvalidColumns)
|
const invalidColumns = Object.values(InvalidColumns)
|
||||||
const finalTables: { [key: string]: any } = {}
|
let finalTables: { [key: string]: any } = {}
|
||||||
const errors: { [key: string]: string } = {}
|
const errors: { [key: string]: string } = {}
|
||||||
for (let [name, table] of Object.entries(tables)) {
|
for (let [name, table] of Object.entries(tables)) {
|
||||||
const schemaFields = Object.keys(table.schema)
|
const schemaFields = Object.keys(table.schema)
|
||||||
|
@ -177,5 +182,9 @@ export function finaliseExternalTables(
|
||||||
// make sure all previous props have been added back
|
// make sure all previous props have been added back
|
||||||
finalTables[name] = copyExistingPropsOver(name, table, entities)
|
finalTables[name] = copyExistingPropsOver(name, table, entities)
|
||||||
}
|
}
|
||||||
|
// sort the tables by name
|
||||||
|
finalTables = Object.entries(finalTables)
|
||||||
|
.sort(([a], [b]) => a.localeCompare(b))
|
||||||
|
.reduce((r, [k, v]) => ({ ...r, [k]: v }), {})
|
||||||
return { tables: finalTables, errors }
|
return { tables: finalTables, errors }
|
||||||
}
|
}
|
||||||
|
|
|
@ -951,10 +951,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/auth@^0.9.185-alpha.9":
|
"@budibase/auth@^0.9.185-alpha.12":
|
||||||
version "0.9.187"
|
version "0.9.188"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.187.tgz#f09d242a79d1f541d047e7779a7fb9fe4e0c13ef"
|
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.188.tgz#82df2fc6e6e3100679a19ced8bab6fe6625f8744"
|
||||||
integrity sha512-UwKR4MSY061B9Mke7FBmb5+lVTZ/2xe2v+qPUtVLCe+LBCNFC+GUOLr3nc42eqfrMYdI4TChArIU0YJc1KLZpw==
|
integrity sha512-U/7MfxbSrsQGGFyTdAicPDafxGeR8zsuh0I17ZujcIiZUoi1eWbLz4/ODITlTj9zxiXojMf+/u68YAF1ABdxDw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@techpass/passport-openidconnect" "^0.3.0"
|
"@techpass/passport-openidconnect" "^0.3.0"
|
||||||
aws-sdk "^2.901.0"
|
aws-sdk "^2.901.0"
|
||||||
|
@ -975,7 +975,7 @@
|
||||||
uuid "^8.3.2"
|
uuid "^8.3.2"
|
||||||
zlib "^1.0.5"
|
zlib "^1.0.5"
|
||||||
|
|
||||||
"@budibase/bbui@^0.9.139", "@budibase/bbui@^0.9.187":
|
"@budibase/bbui@^0.9.139":
|
||||||
version "0.9.187"
|
version "0.9.187"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.187.tgz#84f0a37301cfa41f50eaa335243ac08923d9e34f"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.187.tgz#84f0a37301cfa41f50eaa335243ac08923d9e34f"
|
||||||
integrity sha512-Qy24x99NloRAoG78NMdzoJuX3Gbf+eZdHeYTAeUchljB4o2W2L/Ous8qYBzqigYtVcChjzteSTAZ2jCLq458Vg==
|
integrity sha512-Qy24x99NloRAoG78NMdzoJuX3Gbf+eZdHeYTAeUchljB4o2W2L/Ous8qYBzqigYtVcChjzteSTAZ2jCLq458Vg==
|
||||||
|
@ -1024,14 +1024,63 @@
|
||||||
svelte-flatpickr "^3.2.3"
|
svelte-flatpickr "^3.2.3"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/client@^0.9.185-alpha.9":
|
"@budibase/bbui@^0.9.188":
|
||||||
version "0.9.187"
|
version "0.9.188"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.187.tgz#8ab8a8edc81e65dc5bac15bcbc5058666d0a2c64"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.188.tgz#82c108172fbf81a84378e0ef4ca7cba61ea8d0ba"
|
||||||
integrity sha512-eSNikZWkYxqy0d0zGBY6m8lDlVMNDhr7DFlqyFZOha03Abu1lS+YGKRJUb0TSkx7y4Qmb4hmvU0wj074ToxPJQ==
|
integrity sha512-KevJxHdASITX9RzLvm+b2K3VMwqYFTumvrlpStAP6UIoyPkls0xaAc2KiJJ7Kkq48UkkBtAbOYaMxsFbAaTsbQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/bbui" "^0.9.187"
|
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
||||||
|
"@spectrum-css/actionbutton" "^1.0.1"
|
||||||
|
"@spectrum-css/actiongroup" "^1.0.1"
|
||||||
|
"@spectrum-css/avatar" "^3.0.2"
|
||||||
|
"@spectrum-css/button" "^3.0.1"
|
||||||
|
"@spectrum-css/buttongroup" "^3.0.2"
|
||||||
|
"@spectrum-css/checkbox" "^3.0.2"
|
||||||
|
"@spectrum-css/dialog" "^3.0.1"
|
||||||
|
"@spectrum-css/divider" "^1.0.3"
|
||||||
|
"@spectrum-css/dropzone" "^3.0.2"
|
||||||
|
"@spectrum-css/fieldgroup" "^3.0.2"
|
||||||
|
"@spectrum-css/fieldlabel" "^3.0.1"
|
||||||
|
"@spectrum-css/icon" "^3.0.1"
|
||||||
|
"@spectrum-css/illustratedmessage" "^3.0.2"
|
||||||
|
"@spectrum-css/inputgroup" "^3.0.2"
|
||||||
|
"@spectrum-css/label" "^2.0.10"
|
||||||
|
"@spectrum-css/link" "^3.1.1"
|
||||||
|
"@spectrum-css/menu" "^3.0.1"
|
||||||
|
"@spectrum-css/modal" "^3.0.1"
|
||||||
|
"@spectrum-css/pagination" "^3.0.3"
|
||||||
|
"@spectrum-css/picker" "^1.0.1"
|
||||||
|
"@spectrum-css/popover" "^3.0.1"
|
||||||
|
"@spectrum-css/progressbar" "^1.0.2"
|
||||||
|
"@spectrum-css/progresscircle" "^1.0.2"
|
||||||
|
"@spectrum-css/radio" "^3.0.2"
|
||||||
|
"@spectrum-css/search" "^3.0.2"
|
||||||
|
"@spectrum-css/sidenav" "^3.0.2"
|
||||||
|
"@spectrum-css/statuslight" "^3.0.2"
|
||||||
|
"@spectrum-css/stepper" "^3.0.3"
|
||||||
|
"@spectrum-css/switch" "^1.0.2"
|
||||||
|
"@spectrum-css/table" "^3.0.1"
|
||||||
|
"@spectrum-css/tabs" "^3.0.1"
|
||||||
|
"@spectrum-css/tags" "^3.0.2"
|
||||||
|
"@spectrum-css/textfield" "^3.0.1"
|
||||||
|
"@spectrum-css/toast" "^3.0.1"
|
||||||
|
"@spectrum-css/tooltip" "^3.0.3"
|
||||||
|
"@spectrum-css/treeview" "^3.0.2"
|
||||||
|
"@spectrum-css/typography" "^3.0.1"
|
||||||
|
"@spectrum-css/underlay" "^2.0.9"
|
||||||
|
"@spectrum-css/vars" "^3.0.1"
|
||||||
|
dayjs "^1.10.4"
|
||||||
|
svelte-flatpickr "^3.2.3"
|
||||||
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
|
"@budibase/client@^0.9.185-alpha.12":
|
||||||
|
version "0.9.188"
|
||||||
|
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.188.tgz#c5bf6f3bccdb370b236b9e69e0118334ad3eccfd"
|
||||||
|
integrity sha512-yP2WLWb2yQAwPBxVzpNGjSHpATMZMzcxl2gK6vw662F7YC8xGFHNfZZqEwPvrVwnx+d7LFZR/kJxJOvvk7YCVw==
|
||||||
|
dependencies:
|
||||||
|
"@budibase/bbui" "^0.9.188"
|
||||||
"@budibase/standard-components" "^0.9.139"
|
"@budibase/standard-components" "^0.9.139"
|
||||||
"@budibase/string-templates" "^0.9.187"
|
"@budibase/string-templates" "^0.9.188"
|
||||||
regexparam "^1.3.0"
|
regexparam "^1.3.0"
|
||||||
shortid "^2.2.15"
|
shortid "^2.2.15"
|
||||||
svelte-spa-router "^3.0.5"
|
svelte-spa-router "^3.0.5"
|
||||||
|
@ -1081,10 +1130,10 @@
|
||||||
svelte-apexcharts "^1.0.2"
|
svelte-apexcharts "^1.0.2"
|
||||||
svelte-flatpickr "^3.1.0"
|
svelte-flatpickr "^3.1.0"
|
||||||
|
|
||||||
"@budibase/string-templates@^0.9.185-alpha.9", "@budibase/string-templates@^0.9.187":
|
"@budibase/string-templates@^0.9.185-alpha.12", "@budibase/string-templates@^0.9.188":
|
||||||
version "0.9.187"
|
version "0.9.188"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.187.tgz#e6c7b3b3e9c93014731b0b4e01f482fad6d2b12f"
|
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.188.tgz#f37836ed23dbd2217cb157030ada7cd59f6a2165"
|
||||||
integrity sha512-230yCwmKv6gG0Bi5xUteAKJN7BgYpwepJFEqmF8TrxDaV+qJO55H9br918FbTQ9W9g9Vv0HKU0xNdSfLTrlqhQ==
|
integrity sha512-O/bL0I5OJO9W2OizIe9vBHowCLwwASPBrsGiAIB8L0x6AivYMq8j1mvNRwLXZjpHTjv86bU/LyG/3CP837oDsg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/handlebars-helpers" "^0.11.7"
|
"@budibase/handlebars-helpers" "^0.11.7"
|
||||||
dayjs "^1.10.4"
|
dayjs "^1.10.4"
|
||||||
|
@ -2384,6 +2433,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa"
|
||||||
integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==
|
integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==
|
||||||
|
|
||||||
|
"@types/oracledb@^5.2.1":
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/oracledb/-/oracledb-5.2.1.tgz#b0c64d1ab68f1be6dc153a310ce0e840b8f333df"
|
||||||
|
integrity sha512-xtN24H9bpGB11ZiswZulAKYJ9xcWrF5BOAGFemcfeZkLmw8qAzVm+TAWT20VVLst6kh9VNxinY239S8EKgRBbA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
dotenv "^8.2.0"
|
||||||
|
|
||||||
"@types/prettier@^2.1.5":
|
"@types/prettier@^2.1.5":
|
||||||
version "2.4.2"
|
version "2.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281"
|
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281"
|
||||||
|
@ -2919,7 +2976,7 @@ autolinker@~0.28.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
gulp-header "^1.7.1"
|
gulp-header "^1.7.1"
|
||||||
|
|
||||||
aws-sdk@^2.767.0, aws-sdk@^2.901.0:
|
aws-sdk@^2.767.0:
|
||||||
version "2.1030.0"
|
version "2.1030.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1030.0.tgz#24a856af3d2b8b37c14a8f59974993661c66fd82"
|
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1030.0.tgz#24a856af3d2b8b37c14a8f59974993661c66fd82"
|
||||||
integrity sha512-to0STOb8DsSGuSsUb/WCbg/UFnMGfIYavnJH5ZlRCHzvCFjTyR+vfE8ku+qIZvfFM4+5MNTQC/Oxfun2X/TuyA==
|
integrity sha512-to0STOb8DsSGuSsUb/WCbg/UFnMGfIYavnJH5ZlRCHzvCFjTyR+vfE8ku+qIZvfFM4+5MNTQC/Oxfun2X/TuyA==
|
||||||
|
@ -2934,6 +2991,21 @@ aws-sdk@^2.767.0, aws-sdk@^2.901.0:
|
||||||
uuid "3.3.2"
|
uuid "3.3.2"
|
||||||
xml2js "0.4.19"
|
xml2js "0.4.19"
|
||||||
|
|
||||||
|
aws-sdk@^2.901.0:
|
||||||
|
version "2.1033.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1033.0.tgz#8bd0084142b15cc2ca427483c36c86b27abc046b"
|
||||||
|
integrity sha512-cgcjiuR82bhfBWTffqt6e9+Cn/UgeC6QPQTrlJy3GxwPxChthyrt/h5pekj2l4PLFvETsG10Y6CqQysJEMsncw==
|
||||||
|
dependencies:
|
||||||
|
buffer "4.9.2"
|
||||||
|
events "1.1.1"
|
||||||
|
ieee754 "1.1.13"
|
||||||
|
jmespath "0.15.0"
|
||||||
|
querystring "0.2.0"
|
||||||
|
sax "1.2.1"
|
||||||
|
url "0.10.3"
|
||||||
|
uuid "3.3.2"
|
||||||
|
xml2js "0.4.19"
|
||||||
|
|
||||||
aws-sign2@~0.7.0:
|
aws-sign2@~0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||||
|
@ -4311,6 +4383,11 @@ dotenv@8.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||||
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
||||||
|
|
||||||
|
dotenv@^8.2.0:
|
||||||
|
version "8.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
|
||||||
|
integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
|
||||||
|
|
||||||
double-ended-queue@2.1.0-0:
|
double-ended-queue@2.1.0-0:
|
||||||
version "2.1.0-0"
|
version "2.1.0-0"
|
||||||
resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
|
resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
|
||||||
|
@ -8766,6 +8843,11 @@ optionator@^0.8.1, optionator@^0.8.3:
|
||||||
type-check "~0.3.2"
|
type-check "~0.3.2"
|
||||||
word-wrap "~1.2.3"
|
word-wrap "~1.2.3"
|
||||||
|
|
||||||
|
oracledb@^5.3.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-5.3.0.tgz#a15e6cd16757d8711a2c006a28bd7ecd3b8466f7"
|
||||||
|
integrity sha512-HMJzQ6lCf287ztvvehTEmjCWA21FQ3RMvM+mgoqd4i8pkREuqFWO+y3ovsGR9moJUg4T0xjcwS8rl4mggWPxmg==
|
||||||
|
|
||||||
os-locale@^3.1.0:
|
os-locale@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
|
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
|
||||||
|
|
|
@ -3746,7 +3746,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||||
hash-base "^3.0.0"
|
hash-base "^3.0.0"
|
||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
|
|
||||||
rollup-plugin-node-builtins@^4.3.2:
|
rollup-plugin-node-builtins@^2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/rollup-plugin-node-builtins/-/rollup-plugin-node-builtins-2.1.2.tgz#24a1fed4a43257b6b64371d8abc6ce1ab14597e9"
|
resolved "https://registry.yarnpkg.com/rollup-plugin-node-builtins/-/rollup-plugin-node-builtins-2.1.2.tgz#24a1fed4a43257b6b64371d8abc6ce1ab14597e9"
|
||||||
integrity sha1-JKH+1KQyV7a2Q3HYq8bOGrFFl+k=
|
integrity sha1-JKH+1KQyV7a2Q3HYq8bOGrFFl+k=
|
||||||
|
|
|
@ -295,7 +295,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/auth@^0.9.185-alpha.9":
|
"@budibase/auth@^0.9.185-alpha.12":
|
||||||
version "0.9.188"
|
version "0.9.188"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.188.tgz#82df2fc6e6e3100679a19ced8bab6fe6625f8744"
|
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.188.tgz#82df2fc6e6e3100679a19ced8bab6fe6625f8744"
|
||||||
integrity sha512-U/7MfxbSrsQGGFyTdAicPDafxGeR8zsuh0I17ZujcIiZUoi1eWbLz4/ODITlTj9zxiXojMf+/u68YAF1ABdxDw==
|
integrity sha512-U/7MfxbSrsQGGFyTdAicPDafxGeR8zsuh0I17ZujcIiZUoi1eWbLz4/ODITlTj9zxiXojMf+/u68YAF1ABdxDw==
|
||||||
|
@ -346,7 +346,7 @@
|
||||||
to-gfm-code-block "^0.1.1"
|
to-gfm-code-block "^0.1.1"
|
||||||
year "^0.2.1"
|
year "^0.2.1"
|
||||||
|
|
||||||
"@budibase/string-templates@^0.9.185-alpha.9":
|
"@budibase/string-templates@^0.9.185-alpha.12":
|
||||||
version "0.9.188"
|
version "0.9.188"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.188.tgz#f37836ed23dbd2217cb157030ada7cd59f6a2165"
|
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.188.tgz#f37836ed23dbd2217cb157030ada7cd59f6a2165"
|
||||||
integrity sha512-O/bL0I5OJO9W2OizIe9vBHowCLwwASPBrsGiAIB8L0x6AivYMq8j1mvNRwLXZjpHTjv86bU/LyG/3CP837oDsg==
|
integrity sha512-O/bL0I5OJO9W2OizIe9vBHowCLwwASPBrsGiAIB8L0x6AivYMq8j1mvNRwLXZjpHTjv86bU/LyG/3CP837oDsg==
|
||||||
|
|
Loading…
Reference in New Issue