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 Rest from "./Rest.svelte"
|
||||
import Budibase from "./Budibase.svelte"
|
||||
import Oracle from "./Oracle.svelte"
|
||||
|
||||
export default {
|
||||
BUDIBASE: Budibase,
|
||||
|
@ -24,4 +25,5 @@ export default {
|
|||
MYSQL: MySQL,
|
||||
ARANGODB: ArangoDB,
|
||||
REST: Rest,
|
||||
ORACLE: Oracle,
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export const IntegrationNames = {
|
|||
SQL_SERVER: "SQL Server",
|
||||
AIRTABLE: "Airtable",
|
||||
ARANGODB: "ArangoDB",
|
||||
ORACLE: "Oracle",
|
||||
}
|
||||
|
||||
// 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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.187.tgz#84f0a37301cfa41f50eaa335243ac08923d9e34f"
|
||||
integrity sha512-Qy24x99NloRAoG78NMdzoJuX3Gbf+eZdHeYTAeUchljB4o2W2L/Ous8qYBzqigYtVcChjzteSTAZ2jCLq458Vg==
|
||||
|
@ -969,14 +969,63 @@
|
|||
svelte-flatpickr "^3.2.3"
|
||||
svelte-portal "^1.0.0"
|
||||
|
||||
"@budibase/client@^0.9.185-alpha.9":
|
||||
version "0.9.187"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.187.tgz#8ab8a8edc81e65dc5bac15bcbc5058666d0a2c64"
|
||||
integrity sha512-eSNikZWkYxqy0d0zGBY6m8lDlVMNDhr7DFlqyFZOha03Abu1lS+YGKRJUb0TSkx7y4Qmb4hmvU0wj074ToxPJQ==
|
||||
"@budibase/bbui@^0.9.185-alpha.12", "@budibase/bbui@^0.9.188":
|
||||
version "0.9.188"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.188.tgz#82c108172fbf81a84378e0ef4ca7cba61ea8d0ba"
|
||||
integrity sha512-KevJxHdASITX9RzLvm+b2K3VMwqYFTumvrlpStAP6UIoyPkls0xaAc2KiJJ7Kkq48UkkBtAbOYaMxsFbAaTsbQ==
|
||||
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/string-templates" "^0.9.187"
|
||||
"@budibase/string-templates" "^0.9.188"
|
||||
regexparam "^1.3.0"
|
||||
shortid "^2.2.15"
|
||||
svelte-spa-router "^3.0.5"
|
||||
|
@ -1031,10 +1080,10 @@
|
|||
svelte-apexcharts "^1.0.2"
|
||||
svelte-flatpickr "^3.1.0"
|
||||
|
||||
"@budibase/string-templates@^0.9.185-alpha.9", "@budibase/string-templates@^0.9.187":
|
||||
version "0.9.187"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.187.tgz#e6c7b3b3e9c93014731b0b4e01f482fad6d2b12f"
|
||||
integrity sha512-230yCwmKv6gG0Bi5xUteAKJN7BgYpwepJFEqmF8TrxDaV+qJO55H9br918FbTQ9W9g9Vv0HKU0xNdSfLTrlqhQ==
|
||||
"@budibase/string-templates@^0.9.185-alpha.12", "@budibase/string-templates@^0.9.188":
|
||||
version "0.9.188"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.188.tgz#f37836ed23dbd2217cb157030ada7cd59f6a2165"
|
||||
integrity sha512-O/bL0I5OJO9W2OizIe9vBHowCLwwASPBrsGiAIB8L0x6AivYMq8j1mvNRwLXZjpHTjv86bU/LyG/3CP837oDsg==
|
||||
dependencies:
|
||||
"@budibase/handlebars-helpers" "^0.11.7"
|
||||
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-update="scripts/watchtower-hooks/pre-update.sh"
|
||||
|
@ -16,6 +18,10 @@ COPY . ./
|
|||
RUN yarn
|
||||
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
|
||||
|
||||
# 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-router": "^7.4.2",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/oracledb": "^5.2.1",
|
||||
"@typescript-eslint/parser": "4.28.0",
|
||||
"babel-jest": "^27.0.2",
|
||||
"copyfiles": "^2.4.1",
|
||||
|
@ -150,5 +151,8 @@
|
|||
"typescript": "^4.3.5",
|
||||
"update-dotenv": "^1.1.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"oracledb": "^5.3.0"
|
||||
},
|
||||
"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:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
#- pg_data:/var/lib/postgresql/data/
|
||||
- pg_data:/var/lib/postgresql/data/
|
||||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
|
||||
pgadmin:
|
||||
|
@ -24,5 +24,5 @@ services:
|
|||
ports:
|
||||
- "5050:80"
|
||||
|
||||
#volumes:
|
||||
# pg_data:
|
||||
volumes:
|
||||
pg_data:
|
||||
|
|
|
@ -46,6 +46,7 @@ export enum SourceNames {
|
|||
MYSQL = "MYSQL",
|
||||
ARANGODB = "ARANGODB",
|
||||
REST = "REST",
|
||||
ORACLE = "ORACLE",
|
||||
}
|
||||
|
||||
export enum IncludeRelationships {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export interface IntegrationBase {
|
||||
create?(query: any): Promise<[any]>
|
||||
read?(query: any): Promise<[any]>
|
||||
update?(query: any): Promise<[any]>
|
||||
delete?(query: any): Promise<[any]>
|
||||
create?(query: any): Promise<any[]>
|
||||
read?(query: any): Promise<any[]>
|
||||
update?(query: any): Promise<any[]>
|
||||
delete?(query: any): Promise<any[]>
|
||||
}
|
||||
|
|
|
@ -416,6 +416,8 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
|||
id = results?.[0].id
|
||||
} else if (sqlClient === SqlClients.MY_SQL) {
|
||||
id = results?.insertId
|
||||
} else if (sqlClient === SqlClients.ORACLE) {
|
||||
id = response.outBinds[0][0]
|
||||
}
|
||||
row = processFn(
|
||||
await this.getReturningRow(queryFn, this.checkLookupKeys(id, json))
|
||||
|
@ -428,4 +430,5 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
export default SqlQueryBuilder
|
||||
module.exports = SqlQueryBuilder
|
||||
|
|
|
@ -39,6 +39,13 @@ const INTEGRATIONS = {
|
|||
[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 = {
|
||||
definitions: DEFINITIONS,
|
||||
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,
|
||||
datetime: FieldTypes.DATETIME,
|
||||
tinyint: FieldTypes.BOOLEAN,
|
||||
long: FieldTypes.LONGFORM,
|
||||
number: FieldTypes.NUMBER,
|
||||
binary_float: FieldTypes.NUMBER,
|
||||
binary_double: FieldTypes.NUMBER,
|
||||
}
|
||||
|
||||
export enum SqlClients {
|
||||
MS_SQL = "mssql",
|
||||
POSTGRES = "pg",
|
||||
MY_SQL = "mysql",
|
||||
ORACLE = "oracledb"
|
||||
}
|
||||
|
||||
export function isExternalTable(tableId: string) {
|
||||
|
@ -162,7 +167,7 @@ export function finaliseExternalTables(
|
|||
entities: { [key: string]: any }
|
||||
) {
|
||||
const invalidColumns = Object.values(InvalidColumns)
|
||||
const finalTables: { [key: string]: any } = {}
|
||||
let finalTables: { [key: string]: any } = {}
|
||||
const errors: { [key: string]: string } = {}
|
||||
for (let [name, table] of Object.entries(tables)) {
|
||||
const schemaFields = Object.keys(table.schema)
|
||||
|
@ -177,5 +182,9 @@ export function finaliseExternalTables(
|
|||
// make sure all previous props have been added back
|
||||
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 }
|
||||
}
|
||||
|
|
|
@ -951,10 +951,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/auth@^0.9.185-alpha.9":
|
||||
version "0.9.187"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.187.tgz#f09d242a79d1f541d047e7779a7fb9fe4e0c13ef"
|
||||
integrity sha512-UwKR4MSY061B9Mke7FBmb5+lVTZ/2xe2v+qPUtVLCe+LBCNFC+GUOLr3nc42eqfrMYdI4TChArIU0YJc1KLZpw==
|
||||
"@budibase/auth@^0.9.185-alpha.12":
|
||||
version "0.9.188"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.188.tgz#82df2fc6e6e3100679a19ced8bab6fe6625f8744"
|
||||
integrity sha512-U/7MfxbSrsQGGFyTdAicPDafxGeR8zsuh0I17ZujcIiZUoi1eWbLz4/ODITlTj9zxiXojMf+/u68YAF1ABdxDw==
|
||||
dependencies:
|
||||
"@techpass/passport-openidconnect" "^0.3.0"
|
||||
aws-sdk "^2.901.0"
|
||||
|
@ -975,7 +975,7 @@
|
|||
uuid "^8.3.2"
|
||||
zlib "^1.0.5"
|
||||
|
||||
"@budibase/bbui@^0.9.139", "@budibase/bbui@^0.9.187":
|
||||
"@budibase/bbui@^0.9.139":
|
||||
version "0.9.187"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.187.tgz#84f0a37301cfa41f50eaa335243ac08923d9e34f"
|
||||
integrity sha512-Qy24x99NloRAoG78NMdzoJuX3Gbf+eZdHeYTAeUchljB4o2W2L/Ous8qYBzqigYtVcChjzteSTAZ2jCLq458Vg==
|
||||
|
@ -1024,14 +1024,63 @@
|
|||
svelte-flatpickr "^3.2.3"
|
||||
svelte-portal "^1.0.0"
|
||||
|
||||
"@budibase/client@^0.9.185-alpha.9":
|
||||
version "0.9.187"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.187.tgz#8ab8a8edc81e65dc5bac15bcbc5058666d0a2c64"
|
||||
integrity sha512-eSNikZWkYxqy0d0zGBY6m8lDlVMNDhr7DFlqyFZOha03Abu1lS+YGKRJUb0TSkx7y4Qmb4hmvU0wj074ToxPJQ==
|
||||
"@budibase/bbui@^0.9.188":
|
||||
version "0.9.188"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.188.tgz#82c108172fbf81a84378e0ef4ca7cba61ea8d0ba"
|
||||
integrity sha512-KevJxHdASITX9RzLvm+b2K3VMwqYFTumvrlpStAP6UIoyPkls0xaAc2KiJJ7Kkq48UkkBtAbOYaMxsFbAaTsbQ==
|
||||
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/string-templates" "^0.9.187"
|
||||
"@budibase/string-templates" "^0.9.188"
|
||||
regexparam "^1.3.0"
|
||||
shortid "^2.2.15"
|
||||
svelte-spa-router "^3.0.5"
|
||||
|
@ -1081,10 +1130,10 @@
|
|||
svelte-apexcharts "^1.0.2"
|
||||
svelte-flatpickr "^3.1.0"
|
||||
|
||||
"@budibase/string-templates@^0.9.185-alpha.9", "@budibase/string-templates@^0.9.187":
|
||||
version "0.9.187"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.187.tgz#e6c7b3b3e9c93014731b0b4e01f482fad6d2b12f"
|
||||
integrity sha512-230yCwmKv6gG0Bi5xUteAKJN7BgYpwepJFEqmF8TrxDaV+qJO55H9br918FbTQ9W9g9Vv0HKU0xNdSfLTrlqhQ==
|
||||
"@budibase/string-templates@^0.9.185-alpha.12", "@budibase/string-templates@^0.9.188":
|
||||
version "0.9.188"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.188.tgz#f37836ed23dbd2217cb157030ada7cd59f6a2165"
|
||||
integrity sha512-O/bL0I5OJO9W2OizIe9vBHowCLwwASPBrsGiAIB8L0x6AivYMq8j1mvNRwLXZjpHTjv86bU/LyG/3CP837oDsg==
|
||||
dependencies:
|
||||
"@budibase/handlebars-helpers" "^0.11.7"
|
||||
dayjs "^1.10.4"
|
||||
|
@ -2384,6 +2433,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa"
|
||||
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":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281"
|
||||
|
@ -2919,7 +2976,7 @@ autolinker@~0.28.0:
|
|||
dependencies:
|
||||
gulp-header "^1.7.1"
|
||||
|
||||
aws-sdk@^2.767.0, aws-sdk@^2.901.0:
|
||||
aws-sdk@^2.767.0:
|
||||
version "2.1030.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1030.0.tgz#24a856af3d2b8b37c14a8f59974993661c66fd82"
|
||||
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"
|
||||
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:
|
||||
version "0.7.0"
|
||||
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"
|
||||
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:
|
||||
version "2.1.0-0"
|
||||
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"
|
||||
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:
|
||||
version "3.1.0"
|
||||
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"
|
||||
inherits "^2.0.1"
|
||||
|
||||
rollup-plugin-node-builtins@^4.3.2:
|
||||
rollup-plugin-node-builtins@^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"
|
||||
integrity sha1-JKH+1KQyV7a2Q3HYq8bOGrFFl+k=
|
||||
|
|
|
@ -295,7 +295,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/auth@^0.9.185-alpha.9":
|
||||
"@budibase/auth@^0.9.185-alpha.12":
|
||||
version "0.9.188"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.188.tgz#82df2fc6e6e3100679a19ced8bab6fe6625f8744"
|
||||
integrity sha512-U/7MfxbSrsQGGFyTdAicPDafxGeR8zsuh0I17ZujcIiZUoi1eWbLz4/ODITlTj9zxiXojMf+/u68YAF1ABdxDw==
|
||||
|
@ -346,7 +346,7 @@
|
|||
to-gfm-code-block "^0.1.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"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.188.tgz#f37836ed23dbd2217cb157030ada7cd59f6a2165"
|
||||
integrity sha512-O/bL0I5OJO9W2OizIe9vBHowCLwwASPBrsGiAIB8L0x6AivYMq8j1mvNRwLXZjpHTjv86bU/LyG/3CP837oDsg==
|
||||
|
|
Loading…
Reference in New Issue