Merge branch 'release/v1.1.0'
13
.env.example
|
|
@ -1,10 +1,14 @@
|
|||
# Create .env from this example file and replace values for the environment.
|
||||
# The application expects a separate .env.test for test environment configuration
|
||||
# Get detailed information about each variable here: https://docs.tooljet.com/docs/deployment/env-vars
|
||||
|
||||
TOOLJET_HOST=http://localhost:8082
|
||||
LOCKBOX_MASTER_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
SECRET_KEY_BASE=replace_with_secret_key_base
|
||||
|
||||
## Configure a hostname for the server
|
||||
SERVER_HOST=<hostname>
|
||||
|
||||
# DATABASE CONFIG
|
||||
ORM_LOGGING=
|
||||
PG_DB=<db name>
|
||||
|
|
@ -13,7 +17,12 @@ PG_HOST=<db host>
|
|||
PG_PASS=<db password>
|
||||
|
||||
# Checks every 24 hours to see if a new version of ToolJet is available
|
||||
CHECK_FOR_UPDATES=check_if_updates_are_available
|
||||
# (Enabled by default. Set 0 to disable)
|
||||
CHECK_FOR_UPDATES=0
|
||||
|
||||
# Checks every 24 hours to update app telemetry data to ToolJet hub.
|
||||
# (Telemetry is enabled by default. Set value to true to disable.)
|
||||
# DISABLE_APP_TELEMETRY=false
|
||||
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
|
|
@ -41,4 +50,4 @@ SSO_DISABLE_SIGNUP=
|
|||
SSO_RESTRICTED_DOMAIN=
|
||||
SSO_GOOGLE_OAUTH2_CLIENT_ID=
|
||||
SSO_GIT_OAUTH2_CLIENT_ID=
|
||||
SSO_GIT_OAUTH2_CLIENT_SECRET=
|
||||
SSO_GIT_OAUTH2_CLIENT_SECRET=
|
||||
|
|
|
|||
2
.version
|
|
@ -1 +1 @@
|
|||
1.0.1
|
||||
1.1.0
|
||||
|
|
@ -28,8 +28,32 @@ services:
|
|||
environment:
|
||||
SERVE_CLIENT: "false"
|
||||
command: npm run start:prod
|
||||
# We recommend to use managed postgres service on production for ease of
|
||||
# administration, security and management (high availability, backups, monitoring etc)
|
||||
#
|
||||
# If you'd still want to run posgres with persistent volumes in a docker compose
|
||||
# setup, uncomment the lines below and create postgres_data folder within
|
||||
# the directory.
|
||||
# depends_on:
|
||||
# - postgres
|
||||
|
||||
# postgres:
|
||||
# image: postgres:13
|
||||
# restart: always
|
||||
# ports:
|
||||
# - 5432:5432
|
||||
# volumes:
|
||||
# - postgres:/var/lib/postgresql/data
|
||||
# environment:
|
||||
# - POSTGRES_PASSWORD=postgres
|
||||
|
||||
volumes:
|
||||
# postgres:
|
||||
# driver: local
|
||||
# driver_opts:
|
||||
# o: bind
|
||||
# type: none
|
||||
# device: ${PWD}/postgres_data
|
||||
certs:
|
||||
logs:
|
||||
fallbackcerts:
|
||||
|
|
|
|||
|
|
@ -5,17 +5,16 @@ sidebar_position: 1
|
|||
# Airtable
|
||||
|
||||
|
||||
ToolJet can connect to your Airtable account to read and write data. Airtable API key is required to create an Airtable datasource on ToolJet. You can generate API key by visiting [Airtable account page](https://airtable.com/account).
|
||||
ToolJet can connect to your Airtable account to read and write data. Airtable API key is required to create an Airtable data source on ToolJet. You can generate API key by visiting [Airtable account page](https://airtable.com/account).
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-intro.gif" alt="ToolJet - Datasource Airtable" height="420" />
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-intro.gif" alt="ToolJet - Data source - Airtable" height="420" />
|
||||
|
||||
:::info
|
||||
Airtable API has a rate limit, and at the time of writing this documentation, the limit is five(5) requests per second per base. You can read more about rate limits here [Airtable API]( https://airtable.com/api ).
|
||||
:::
|
||||
|
||||
:::tip
|
||||
This guide assumes that you have already gone through [Adding a datasource
|
||||
](/docs/tutorial/adding-a-datasource) tutorial.
|
||||
This guide assumes that you have already gone through [Adding a data source](/docs/tutorial/adding-a-datasource) tutorial.
|
||||
:::
|
||||
|
||||
Supported queries:
|
||||
|
|
@ -84,7 +83,7 @@ Required parameters:
|
|||
- Table name
|
||||
- Record ID
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-retrive-record.png" alt="ToolJet - Datasource Airtable Retrieve Operation" height="420" />
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-retrive-record.png" alt="ToolJet - Data source - Airtable Retrieve Operation" height="420" />
|
||||
|
||||
Example response from Airtable:
|
||||
|
||||
|
|
@ -106,7 +105,7 @@ Required parameters:
|
|||
- Table name
|
||||
- Records
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-create.png" alt="ToolJet - Datasource Airtable Create Operarion" height="420"/>
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-create.png" alt="ToolJet - Data source - Airtable Create Operarion" height="420"/>
|
||||
|
||||
#### Example Records:
|
||||
|
||||
|
|
@ -167,11 +166,11 @@ Required parameters:
|
|||
- Table name
|
||||
- Record ID
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-update.png" alt="ToolJet - Datasource Airtable Update Operarion" height="420"/>
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-update.png" alt="ToolJet - Data source - Airtable Update Operarion" height="420"/>
|
||||
|
||||
#### Example body:
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-update-example-body.png" alt="ToolJet - Datasource Airtable Update Operarion Body" height="200" width="650" />
|
||||
<img class="screenshot-full" src="/img/datasource-reference/airtable/airtable-update-example-body.png" alt="ToolJet - Data source - Airtable Update Operarion Body" height="200" width="650" />
|
||||
|
||||
|
||||
Click on the `run` button to run the query.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
---
|
||||
|
||||
sidebar_position: 18
|
||||
|
||||
---
|
||||
|
||||
# BigQuery
|
||||
|
|
@ -49,7 +46,7 @@ The json looks like :
|
|||
|
||||
|
||||
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the API is accessible to ToolJet server. Click on 'Save' button to save the datasource.
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the API is accessible to ToolJet server. Click on 'Save' button to save the data source.
|
||||
|
||||
|
||||
|
||||
|
|
@ -57,7 +54,7 @@ Click on 'Test connection' button to verify if the credentials are correct and t
|
|||
|
||||
|
||||
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource. Select the operation that you want to perform and click 'Save' to save the query.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source. Select the operation that you want to perform and click 'Save' to save the query.
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ sidebar_position: 4
|
|||
|
||||
# Custom JavaScript
|
||||
|
||||
You can write custom JavaScript code to interact with components and queries. To do that, you just need to create a new query and select **Run JavaScript Code** from the datasources dropdown.
|
||||
You can write custom JavaScript code to interact with components and queries. To do that, you just need to create a new query and select **Run JavaScript Code** from the data sources dropdown.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ It is recommended to create a new IAM user for the database so that you can cont
|
|||
|
||||
<img src="/img/datasource-reference/dynamo-connect.png" alt="ToolJet - Dynamo connection" height="250"/>
|
||||
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the datasource.
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the data source.
|
||||
|
||||
## Querying DynamoDB
|
||||
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource. Select the operation that you want to perform and click 'Save' to save the query.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source. Select the operation that you want to perform and click 'Save' to save the query.
|
||||
|
||||
<img src="/img/datasource-reference/dynamo-query.png" alt="ToolJet - Dynamo query" height="250"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ ToolJet requires the following to connect to your Elasticsearch cluster:
|
|||
|
||||
## Querying Firestore
|
||||
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source.
|
||||
Select the operation that you want to perform on Firestore and click 'Save' to save the query.
|
||||
|
||||
:::tip
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ To generate a new key, check out Firestore's official documentation: [https://cl
|
|||
|
||||
Once the key is downloaded, click on `+` button of data sources panel at the left-bottom corner of the app editor. Select Firestore from the modal that pops up. Paste the key in the field for GCP key. Click on 'Test connection' button to verify if the service account can access Firestore from ToolJet server. Click on 'Save' button to save the datasource.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/firestore/firestore-intro.gif" alt="ToolJet - Datasource Firestore" height="420" />
|
||||
<img class="screenshot-full" src="/img/datasource-reference/firestore/firestore-intro.gif" alt="ToolJet - Data source - Firestore" height="420" />
|
||||
|
||||
## Querying Firestore
|
||||
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/firestore/firestore-query.png" alt="ToolJet - Firestore connection" height="420"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ You can follow the [google documentation](https://cloud.google.com/docs/authenti
|
|||
|
||||

|
||||
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the datasource.
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the data source.
|
||||
|
||||
## Querying GCS
|
||||
|
||||
Click on `+` button of the **query manager** at the bottom panel of the editor and select the datasource added in the previous step as the datasource. Select the operation that you want to perform and click **Save** to save the query.
|
||||
Click on `+` button of the **query manager** at the bottom panel of the editor and select the data source added in the previous step as the data source. Select the operation that you want to perform and click **Save** to save the query.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,104 @@ sidebar_position: 8
|
|||
|
||||
ToolJet can connect to Google Sheet using OAuth 2.0, which helps us to limit an application's access to a user's account.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/googlesheet.gif" alt="ToolJet - ToolJet - Datasource Google Sheets" height="420" />
|
||||
## Authorization Scopes
|
||||
|
||||
You can create a Google Sheets data source with one of either of the two permission scopes :
|
||||
1. **Read Only**
|
||||
2. **Read and Write**
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## Operations
|
||||
|
||||
Using Google sheets data source you can perfom several operations from your applications like:
|
||||
|
||||
1. **[Read data from a sheet](/docs/data-sources/google.sheets#read-data-from-a-sheet)**
|
||||
2. **[Append data to a sheet](/docs/data-sources/google.sheets#append-data-to-a-sheet)**
|
||||
3. **[Update single row of a sheet](/docs/data-sources/google.sheets#update-single-row-of-a-sheet)**
|
||||
4. **[Delete row from a sheet](/docs/data-sources/google.sheets#delete-row-from-a-sheet)**
|
||||
5. **[Get spreadsheet info](/docs/data-sources/google.sheets#get-spreadsheet-info)**
|
||||
|
||||
### Read data from a sheet
|
||||
|
||||
This operation returns the table data from the spreadsheet in the form of json object.
|
||||
|
||||
| Fields | description |
|
||||
| ----------- | ----------- |
|
||||
| Spreadsheet ID | It is mandatory to enter the spreadsheet-id. The spreadsheet-id can be found in the URL of the spreadsheet. Example URL: https://docs.google.com/spreadsheets/d/1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM/edit#gid=0 - in this URL, the `1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM` is the spreadsheet-id. |
|
||||
| Range | This is optional. You can specify the range of cells in this field. If left empty, it will select the range `A1:Z500`. |
|
||||
| Sheet | This is optional. You can specify `sheet name` if it has more than 1 sheets, else it will automatically choose the first sheet. |
|
||||
|
||||
|
||||
### Authorization Scopes
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||
You can create a Google Sheets datasource with one of either of the two permission scopes :
|
||||
1. Read Only
|
||||
2. Read and Write
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
### Append data to a sheet
|
||||
|
||||
You can add more rows to the table using the append operation.
|
||||
|
||||
| Fields | description |
|
||||
| ----------- | ----------- |
|
||||
| Spreadsheet ID | It is mandatory to enter the spreadsheet-id. The spreadsheet-id can be found in the URL of the spreadsheet. Example URL: https://docs.google.com/spreadsheets/d/1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM/edit#gid=0 - in this URL, the `1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM` is the spreadsheet-id. |
|
||||
| Sheet | This is optional. You can specify `sheet name` if it has more than 1 sheets, else it will automatically choose the first sheet. |
|
||||
| Rows | Enter the row data in the json array form. Each object in an array will represent a single row. Example: `[ {"name":"John", "email":"John@tooljet.com"},{...},{...} ]` In each object, the `key` represents the **column name** and the `value` represents the **cell data**. |
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
### Update single row of a sheet
|
||||
|
||||
You can update the existing data in sheet using this operation.
|
||||
|
||||
| Fields | description |
|
||||
| ----------- | ----------- |
|
||||
| Spreadsheet ID | It is mandatory to enter the spreadsheet-id. The spreadsheet-id can be found in the URL of the spreadsheet. Example URL: https://docs.google.com/spreadsheets/d/1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM/edit#gid=0 - in this URL, the `1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM` is the spreadsheet-id. |
|
||||
| Where | Enter the column name such as `id` for choosing a row. |
|
||||
| Operator | Choose the `===` operator to check the equality. |
|
||||
| Value | Enter the any `id` number/name that you want to update. |
|
||||
| Rows | Enter the row data. Example: `{{({id: components.textinput4.value, company: components.textinput1.value, position: components.textinput2.value, url: components.textinput3.value, 'date-applied': components.datepicker1.value, status: components.dropdown1.value})}}` |
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
### Delete row from a sheet
|
||||
|
||||
Use this operation delete a specific row from the sheet.
|
||||
|
||||
| Fields | description |
|
||||
| ----------- | ----------- |
|
||||
| Spreadsheet ID | It is mandatory to enter the spreadsheet-id. The spreadsheet-id can be found in the URL of the spreadsheet. Example URL: https://docs.google.com/spreadsheets/d/1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM/edit#gid=0 - in this URL, the `1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM` is the spreadsheet-id. |
|
||||
| GID | You'll find the GID in the end of the URL of spreadsheet. In the example mentioned above, the GID is 0 |
|
||||
| Delete row number | Just enter the row number that you want to delete. |
|
||||
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
### Get spreadsheet info
|
||||
|
||||
This operation can be used to get some basic information of the spreadsheet such as the number of sheets, theme, time-zone, format, and url etc.
|
||||
|
||||
Here is the `Preview` of the query that used the get spreadsheet info operation.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
|
@ -26,10 +26,10 @@ The following optional parameters are also supported:
|
|||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/graphql/add-source.gif" alt="ToolJet - GraphQL connection" height="420"/>
|
||||
|
||||
Click on the 'Save' button to save the datasource.
|
||||
Click on the 'Save' button to save the data source.
|
||||
|
||||
## Querying GraphQL
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the GraphQL endpoint added in the previous step as the datasource.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the GraphQL endpoint added in the previous step as the data source.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/graphql-query.png" alt="ToolJet - GraphQL connection" height="420"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ ToolJet requires the following to connect to your DynamoDB:
|
|||
|
||||
</div>
|
||||
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the datasource.
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the data source.
|
||||
|
||||
## Querying Minio
|
||||
|
||||
Click on `+` button of the **query manager** at the bottom panel of the editor and select the datasource added in the previous step as the datasource. Select the operation that you want to perform and click **Save** to save the query.
|
||||
Click on `+` button of the **query manager** at the bottom panel of the editor and select the data source added in the previous step as the data source. Select the operation that you want to perform and click **Save** to save the query.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ It is recommended to create a new MongoDB user so that you can control the acces
|
|||
|
||||
<img src="/img/datasource-reference/mo-connect.png" alt="ToolJet - Mongo connection" height="250"/>
|
||||
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the datasource.
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the data source.
|
||||
|
||||
## Querying MongoDB
|
||||
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource. Select the operation that you want to perform and click 'Save' to save the query.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source. Select the operation that you want to perform and click 'Save' to save the query.
|
||||
|
||||
<img src="/img/datasource-reference/mo-query.png" alt="ToolJet - Mongo query" height="250"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ ToolJet requires the following to connect to your PostgreSQL database.
|
|||
|
||||
It is recommended to create a new database user so that you can control the access levels of ToolJet.
|
||||
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the datasource.
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the data source.
|
||||
|
||||
<img src="/img/datasource-reference/mssql/connect.gif" alt="ToolJet - Redis connection" height="420"/>
|
||||
|
||||
|
||||
## Querying SQL Server / Azure SQL databases
|
||||
Click on '+' button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource.
|
||||
Click on '+' button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source.
|
||||
|
||||
Click on the 'run' button to run the query. NOTE: Query should be saved before running.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ ToolJet can connect to MySQL databases to read and write data.
|
|||
|
||||
ToolJet requires the following to connect to your MySQL database. Please make sure the host/ip of the database is accessible from your VPC if you have self-hosted ToolJet. If you are using ToolJet cloud, please whitelist our IP.
|
||||
|
||||
To add a new MySQL database, click on the `+` button on data sources panel at the left-bottom corner of the app editor. Select MySQL from the modal that pops up.
|
||||
To add a new MySQL database, click on the `+` button on data sources panel at left sidebar in the app editor. Select MySQL from the modal that pops up.
|
||||
|
||||
ToolJet requires the following to connect to your MySQL database.
|
||||
|
||||
|
|
@ -21,17 +21,48 @@ ToolJet requires the following to connect to your MySQL database.
|
|||
|
||||
It is recommended to create a new MySQL database user so that you can control the access levels of ToolJet.
|
||||
|
||||
<img src="/img/datasource-reference/mysql.png" alt="ToolJet - Redis connection" height="250"/>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the datasource.
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the data source.
|
||||
|
||||
## Querying MySQL
|
||||
Click on `+` button of the query manager at the bottom panel of the editor.
|
||||
|
||||
<img src="/img/datasource-reference/mysql-query.png" alt="ToolJet - Redis connection" height="250"/>
|
||||
Once you have added a MySQL data source, click on `+` button of the query manager to create a new query. There are two modes by which you can query SQL:
|
||||
|
||||
Click on the 'run' button to run the query. NOTE: Query should be saved before running.
|
||||
1. **[SQL mode](/docs/data-sources/mysql#sql-mode)**
|
||||
2. **[GUI mode](/docs/data-sources/mysql#gui-mode)**
|
||||
|
||||
#### SQL mode
|
||||
|
||||
SQL mode can be used to write raw SQL queries. Select SQL mode from the dropdown and enter the SQL query in the editor. Click on the `run` button to run the query.
|
||||
|
||||
**NOTE**: Query should be saved before running.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
|
||||
#### GUI mode
|
||||
|
||||
GUI mode can be used to query MySQL database without writing queries. Select GUI mode from the dropdown and then choose the operation **Bulk update using primary key**. Enter the **Table** name and **Primary key column** name. Now, in the editor enter the records in the form of an array of objects.
|
||||
|
||||
**Example**: `{{ [ {id: 1, channel: 33}, {id:2, channel:24} ] }}`
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
Click on the **run** button to run the query. **NOTE**: Query should be saved before running.
|
||||
|
||||
:::tip
|
||||
Query results can be transformed using transformations. Read our transformations documentation to see how: [link](/docs/tutorial/transformations)
|
||||
Query results can be transformed using transformations. Read our transformations documentation to see how: **[link](/docs/tutorial/transformations)**
|
||||
:::
|
||||
|
|
@ -4,14 +4,13 @@ sidebar_position: 13
|
|||
|
||||
# PostgreSQL
|
||||
|
||||
|
||||
ToolJet can connect to PostgreSQL databases to read and write data.
|
||||
|
||||
## Connection
|
||||
|
||||
Please make sure the host/ip of the database is accessible from your VPC if you have self-hosted ToolJet. If you are using ToolJet cloud, please whitelist our IP.
|
||||
|
||||
To add a new PostgreSQL database, click on the '+' button on data sources panel at the left-bottom corner of the app editor. Select PostgreSQL from the modal that pops up.
|
||||
To add a new PostgreSQL database, click on the `+` button on data sources panel at the left-bottom corner of the app editor. Select PostgreSQL from the modal that pops up.
|
||||
|
||||
ToolJet requires the following to connect to your PostgreSQL database.
|
||||
|
||||
|
|
@ -20,19 +19,44 @@ ToolJet requires the following to connect to your PostgreSQL database.
|
|||
- **Username**
|
||||
- **Password**
|
||||
|
||||
It is recommended to create a new PostgreSQL database user so that you can control the access levels of ToolJet.
|
||||
It is recommended to create a new PostgreSQL database user so that you can control the access levels of ToolJet.
|
||||
|
||||
<img src="/img/datasource-reference/pg-connect.png" alt="ToolJet - Redis connection" height="250"/>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the datasource.
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the data source.
|
||||
|
||||
## Querying PostgreSQL
|
||||
Click on '+' button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource. PostgreSQL query editor has two modes, SQL & GUI. SQL mode can be used to write raw SQL queries and GUI mode can be used to query your PostgreSQL database without writing queries.
|
||||
|
||||
<img src="/img/datasource-reference/pg-query.png" alt="ToolJet - Redis connection" height="250"/>
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source. PostgreSQL query editor has two modes, SQL & GUI. **[SQL mode](/docs/data-sources/postgresql#sql-mode)** can be used to write raw SQL queries and **[GUI mode](/docs/data-sources/postgresql#gui-mode)** can be used to query your PostgreSQL database without writing queries.
|
||||
|
||||
Click on the 'run' button to run the query. NOTE: Query should be saved before running.
|
||||
#### SQL mode
|
||||
|
||||
Select SQL mode from the dropdown and enter the query in the editor. Click on the `run` button to run the query.
|
||||
|
||||
**NOTE**: Query should be saved before running.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
#### GUI mode
|
||||
|
||||
Select GUI mode from the dropdown and then choose the operation **Bulk update using primary key**. Enter the **Table** name and **Primary key column** name. Now, in the editor enter the **records** in the form of an array of objects.
|
||||
|
||||
Click on the `run` button to run the query. **NOTE**: Query should be saved before running.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
:::tip
|
||||
Query results can be transformed using transformations. Read our transformations documentation to see how: [link](/docs/tutorial/transformations)
|
||||
Query results can be transformed using transformations. Read our transformations documentation to see how: **[link](/docs/tutorial/transformations)**
|
||||
:::
|
||||
|
|
@ -16,7 +16,7 @@ ToolJet requires the following to connect to your Redis instances.
|
|||
- **Username**
|
||||
- **Password**
|
||||
|
||||
Click on "Test" button to test the connection and click "Save" to save the datasource.
|
||||
Click on "Test" button to test the connection and click "Save" to save the data source.
|
||||
|
||||
## Redis Queries
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ ToolJet can connect to any REST endpoint available.
|
|||
|
||||
## Connection
|
||||
|
||||
To add a new REST API datasource, click the Datasource manager icon on the left-sidebar of the app builder and click on the `Add datasource` button, then select REST API from the modal that pops up.
|
||||
Click on the 'Save' button to save the datasource.
|
||||
To add a new REST API datasource, click the Datasources manager icon on the left-sidebar of the app builder and click on the `Add datasource` button, then select REST API from the modal that pops up.
|
||||
Click on the 'Save' button to save the data source.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/rest-api/rest-api.gif" alt="ToolJet - Datasource - REST API" height="420"/>
|
||||
<img class="screenshot-full" src="/img/datasource-reference/rest-api/rest-api.gif" alt="ToolJet - Data source - REST API" height="420"/>
|
||||
|
||||
ToolJet requires the following to connect to a REST API datasource.
|
||||
|
||||
|
|
@ -29,10 +29,10 @@ The following optional parameters are also supported:
|
|||
REST HTTP methods that are supported are **GET, POST, PUT, PATCH & DELETE**.
|
||||
:::
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/rest-api/rest-api-values.gif" alt="ToolJet - Datasource - REST API" height="420"/>
|
||||
<img class="screenshot-full" src="/img/datasource-reference/rest-api/rest-api-values.gif" alt="ToolJet - Data source - REST API" height="420"/>
|
||||
|
||||
## Querying REST API
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the REST API endpoint added in the previous step as the datasource.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the REST API endpoint added in the previous step as the data source.
|
||||
|
||||
Click on the 'run' button to run the query.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ ToolJet can connect to Amazon S3 buckets and perform various operation on them.
|
|||
|
||||
## Connection
|
||||
|
||||
To add a new S3 source, click on the Add or edit datasource icon on the left sidebar of the app editor and click on `Add datasource` button. Select AWS S3 from the modal that pops up.
|
||||
To add a new S3 source, go to the **Datasources manager** on the left sidebar of the app editor and click on `Add datasource` button. Select **AWS S3** from the modal that pops up.
|
||||
|
||||
ToolJet requires the following to connect to your DynamoDB:
|
||||
|
||||
|
|
@ -20,11 +20,11 @@ It is recommended to create a new IAM user for the database so that you can cont
|
|||
|
||||

|
||||
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the datasource.
|
||||
Click on **Test connection** button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on **Save** button to save the data source.
|
||||
|
||||
## Querying AWS S3
|
||||
|
||||
Click on `+` button of the **query manager** at the bottom panel of the editor and select the datasource added in the previous step as the datasource. Select the operation that you want to perform and click **Save** to save the query.
|
||||
Click on `+` button of the **query manager** at the bottom panel of the editor and select the data source added in the previous step as the data source. Select the operation that you want to perform and click **Save** to save the query.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ sidebar_position: 16
|
|||
|
||||
ToolJet can connect to your SendGrid account to send emails.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/sendgrid/sendgrid-datasource.png" alt="ToolJet - Datasource SendGrid" height="420" />
|
||||
<img class="screenshot-full" src="/img/datasource-reference/sendgrid/sendgrid-datasource.png" alt="ToolJet - Data source - SendGrid" height="420" />
|
||||
|
||||
:::info
|
||||
The SendGrid API Datasource supports for interaction with the mail endpoint of the [SendGrid v3 API](https://docs.sendgrid.com/api-reference/how-to-use-the-sendgrid-v3-api/authentication).
|
||||
:::
|
||||
|
||||
## Connection
|
||||
To add a new SendGrid API datasource, click the Datasource manager icon on the left-sidebar of the app builder and click on the `Add datasource` button, then select SendGrid API from the modal that pops up.
|
||||
To add a new SendGrid API datasource, click the **Datasource manager** icon on the left-sidebar of the app builder and click on the `Add datasource` button, then select SendGrid API from the modal that pops up.
|
||||
|
||||
Enter your **SendGrid API key** in the "API key" field.
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ Enter your **SendGrid API key** in the "API key" field.
|
|||
SendGrid API key is required to create an SendGrid datasource on ToolJet. You can generate API key by visiting [SendGrid account page](https://app.sendgrid.com/settings/api_keys).
|
||||
:::
|
||||
|
||||
Click on the 'Save' button to save the datasource.
|
||||
Click on the 'Save' button to save the data source.
|
||||
|
||||
## Supported operations
|
||||
1. Email service
|
||||
|
|
|
|||
35
docs/docs/data-sources/snowflake.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Snowflake
|
||||
|
||||
ToolJet can connect to Snowflake databases to read and write data.
|
||||
|
||||
- [Connection](#connection)
|
||||
- [Getting Started](#querying-snowflake)
|
||||
|
||||
## Connection
|
||||
|
||||
Please make sure the host/ip of the database is accessible from your VPC if you have self-hosted ToolJet. If you are using ToolJet cloud, please whitelist our IP. You can find snowflake docs on network policies [https://docs.snowflake.com/en/user-guide/network-policies.html](here)
|
||||
|
||||
|
||||
To add a new Snowflake database, click on the '+' button on data sources panel at the left-bottom corner of the app editor. Select Snowflake from the modal that pops up.
|
||||
|
||||
ToolJet requires the following to connect to your Snowflake database.
|
||||
|
||||
- **Account**
|
||||
- **Username**
|
||||
- **Password**
|
||||
|
||||
You can also configure for [additional optional parameters](https://docs.snowflake.com/en/user-guide/nodejs-driver-use.html#additional-connection-options).
|
||||
|
||||
<img src="/img/datasource-reference/snowflake/snowflake-connect.png" alt="ToolJet - Snowflake connection" height="250"/>
|
||||
|
||||
## Querying Snowflake
|
||||
|
||||
Click on '+' button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource. Query manager then can be used to write raw SQL queries.
|
||||
|
||||
<img src="/img/datasource-reference/snowflake/snowflake-query.png" alt="ToolJet - Snowflake query" height="250"/>
|
||||
|
||||
Click on the 'run' button to run the query. NOTE: Query should be saved before running.
|
||||
|
||||
:::tip
|
||||
Query results can be transformed using transformations. Read our transformations documentation to see how: [link](/docs/tutorial/transformations)
|
||||
:::
|
||||
|
|
@ -11,7 +11,7 @@ sidebar_label: Architecture
|
|||
ToolJet have two main components: **ToolJet Server** and **ToolJet Client**.
|
||||
|
||||
1. ### ToolJet Server
|
||||
ToolJet server is a Node.js API application. Server is responsible for authentication, authorization, persisting application definitions, running queries, storing datasource credentials securely and more.
|
||||
ToolJet server is a Node.js API application. Server is responsible for authentication, authorization, persisting application definitions, running queries, storing data source credentials securely and more.
|
||||
|
||||
Dependencies:
|
||||
- PostgreSQL - ToolJet server persists data to a postgres database.
|
||||
|
|
|
|||
|
|
@ -14,17 +14,6 @@ Both the ToolJet server and client requires some environment variables to start
|
|||
| ------------ | --------------------------------------------------------------- |
|
||||
| TOOLJET_HOST | the public URL of ToolJet client ( eg: https://app.tooljet.com ) |
|
||||
|
||||
#### Database configuration ( required )
|
||||
|
||||
ToolJet server uses PostgreSQL as the database.
|
||||
|
||||
| variable | description |
|
||||
| -------- | ---------------------- |
|
||||
| PG_HOST | postgres database host |
|
||||
| PG_DB | name of the database |
|
||||
| PG_USER | username |
|
||||
| PG_PASS | password |
|
||||
|
||||
#### Lockbox configuration ( required )
|
||||
|
||||
ToolJet server uses lockbox to encrypt datasource credentials. You should set the environment variable `LOCKBOX_MASTER_KEY` with a 32 byte hexadecimal string.
|
||||
|
|
@ -40,6 +29,37 @@ For `LOCKBOX_MASTER_KEY` use `openssl rand -hex 32`
|
|||
For `SECRET_KEY_BASE` use `openssl rand -hex 64`
|
||||
:::
|
||||
|
||||
#### Database configuration ( required )
|
||||
|
||||
ToolJet server uses PostgreSQL as the database.
|
||||
|
||||
| variable | description |
|
||||
| -------- | ---------------------- |
|
||||
| PG_HOST | postgres database host |
|
||||
| PG_DB | name of the database |
|
||||
| PG_USER | username |
|
||||
| PG_PASS | password |
|
||||
|
||||
#### Check for updates ( optional )
|
||||
|
||||
Self-hosted version of ToolJet pings our server to fetch the latest product updates every 24 hours. You can disable this by setting the value of `CHECK_FOR_UPDATES` environment variable to `0`. This feature is enabled by default.
|
||||
|
||||
#### Comment feature enable ( optional )
|
||||
|
||||
Use this environment variable to enable/disable the feature that allows you to add comments on the canvas.
|
||||
|
||||
| variable | value |
|
||||
| -------- | ---------------------- |
|
||||
| COMMENT_FEATURE_ENABLE | `true` or `false` |
|
||||
|
||||
#### Server Host ( optional )
|
||||
|
||||
You can specify a different server for backend if it is hosted on another server.
|
||||
|
||||
| variable | value |
|
||||
| -------- | ---------------------- |
|
||||
| SERVER_HOST | Configure a hostname for the server as a proxy pass. If no value is set, it defaults to `server`. |
|
||||
|
||||
#### Disabling signups ( optional )
|
||||
|
||||
If you want to restrict the signups and allow new users only by invitations, set the environment variable `DISABLE_SIGNUPS` to `true`.
|
||||
|
|
@ -75,7 +95,7 @@ ToolJet uses SMTP services to send emails ( Eg: invitation email when you add ne
|
|||
|
||||
#### Slack configuration ( optional )
|
||||
|
||||
If your ToolJet installation requires Slack as a datasource, you need to create a Slack app and set the following environment variables:
|
||||
If your ToolJet installation requires Slack as a data source, you need to create a Slack app and set the following environment variables:
|
||||
|
||||
| variable | description |
|
||||
| ------------------- | ------------------------------ |
|
||||
|
|
@ -84,7 +104,7 @@ If your ToolJet installation requires Slack as a datasource, you need to create
|
|||
|
||||
#### Google OAuth ( optional )
|
||||
|
||||
If your ToolJet installation needs access to datasources such as Google sheets, you need to create OAuth credentials from Google Cloud Console.
|
||||
If your ToolJet installation needs access to data sources such as Google sheets, you need to create OAuth credentials from Google Cloud Console.
|
||||
|
||||
| variable | description |
|
||||
| -------------------- | ------------- |
|
||||
|
|
@ -105,16 +125,27 @@ Specify application monitoring vendor. Currently supported values - `sentry`.
|
|||
|
||||
| variable | description |
|
||||
| ---------- | ----------------------------------------- |
|
||||
| APM VENDOR | Application performance monitoring vendor |
|
||||
| APM_VENDOR | Application performance monitoring vendor |
|
||||
|
||||
#### SENTRY DNS ( optional )
|
||||
|
||||
DSN tells a Sentry SDK where to send events so the events are associated with the correct project
|
||||
| variable | description |
|
||||
| ---------- | ----------------------------------------- |
|
||||
| SENTRY_DNS | DSN tells a Sentry SDK where to send events so the events are associated with the correct project |
|
||||
|
||||
#### SENTRY DEBUG ( optional )
|
||||
|
||||
Prints logs for sentry. Supported values: `true` | `false`
|
||||
Default value is `false`
|
||||
Prints logs for sentry.
|
||||
|
||||
| variable | description |
|
||||
| ---------- | ----------------------------------------- |
|
||||
| SENTRY_DEBUG | `true` or `false`. Default value is `false` |
|
||||
|
||||
#### SSO ( optional )
|
||||
|
||||
:::info
|
||||
We currently support GitHub and Google SSO. Check out docs for **[GitHub SSO](/docs/sso/github)** and **[Google SSO](/docs/sso/google)** for more information on respective environment variables.
|
||||
:::
|
||||
|
||||
#### Server URL ( optional)
|
||||
|
||||
|
|
@ -136,6 +167,11 @@ Tooljet needs to be configured for custom CA certificate to be able to trust and
|
|||
| ------------------ | ----------------------------------------------------------------- |
|
||||
| NODE_EXTRA_CA_CERTS | absolute path to certifcate PEM file ( eg: /ToolJet/ca/cert.pem ) |
|
||||
|
||||
|
||||
#### Disable telemetry ( optional )
|
||||
|
||||
Pings our server to update the total user count every 24 hours. You can disable this by setting the value of `DISABLE_TOOLJET_TELEMETRY` environment variable to `true`. This feature is enabled by default.
|
||||
|
||||
## ToolJet client
|
||||
|
||||
#### Server URL ( optionally required )
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ Google Cloud Platform provides access to more than 350 APIs and Services that ca
|
|||
|
||||
Let's follow the steps to authorize ToolJet to access your Google profile data:
|
||||
|
||||
- Select **add datasource** from the left sidebar, and choose **REST API** from the dialog window.
|
||||
- Select **add data source** from the left sidebar, and choose **REST API** from the dialog window.
|
||||
|
||||
:::info
|
||||
You can rename the datasource by clicking on its default name `REST API`
|
||||
You can rename the data source by clicking on its default name `REST API`
|
||||
:::
|
||||
|
||||
- In the **URL** field, enter the base URL `https://www.googleapis.com/oauth2/v1/userinfo`; the base URL specifies the network address of the API service.
|
||||
|
|
@ -44,7 +44,7 @@ You can rename the datasource by clicking on its default name `REST API`
|
|||
| client_id | **Client ID** |
|
||||
| redirect_uri | `http://localhost:8082/oauth2/authorize` if using ToolJet locally or enter this `https://app.tooljet.com/oauth2/authorize` if using ToolJet Cloud. |
|
||||
|
||||
- Keep the default selection for **Client Authentication** and **Save** the datasource.
|
||||
- Keep the default selection for **Client Authentication** and **Save** the data source.
|
||||
|
||||
<img class="screenshot-full" src="/img/how-to/oauth2-authorization/restapi.png" alt="ToolJet - How To - REST API authentication using OAuth 2.0"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ sidebar_label: Security
|
|||
|
||||
## Data storage
|
||||
|
||||
ToolJet does not store data returned from your data sources. ToolJet server acts as a proxy and passes the data as it is to the ToolJet client. The credentials for the data sources are hanlded by the server and never exposed to the client. For example, if you are making an API request, the query is run from the server and not from the frontend.
|
||||
ToolJet does not store data returned from your data sources. ToolJet server acts as a proxy and passes the data as it is to the ToolJet client. The credentials for the data sources are handled by the server and never exposed to the client. For example, if you are making an API request, the query is run from the server and not from the frontend.
|
||||
|
||||
## Datasource credentials
|
||||
All the datasource credentials are securely encrypted using `aes-256-gcm`. The credentials are never exposed to the frontend ( ToolJet client ).
|
||||
|
|
@ -19,4 +19,4 @@ All the datasource credentials are securely encrypted using `aes-256-gcm`. The c
|
|||
- **Whitelisted IPs**: If you are using ToolJet cloud, you can whitelist our IP address (3.129.198.40) so that your datasources are not exposed to the public.
|
||||
- **Backups**: ToolJet cloud is hosted on AWS using EKS with autoscaling and regular backups.
|
||||
|
||||
If you notice a security vulnerability, please let the team know by sending an email to `security@tooljet.com`.
|
||||
If you notice a security vulnerability, please let the team know by sending an email to `security@tooljet.com`.
|
||||
|
|
|
|||
|
|
@ -2,36 +2,36 @@
|
|||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Adding a datasource
|
||||
# Adding a data source
|
||||
|
||||
:::tip
|
||||
The datasources are created on app level and not on organization level.
|
||||
The data sources are created on app level and not on organization level.
|
||||
:::
|
||||
|
||||
Datasource manager is on the left-sidebar of the app builder. To add a new datasource, click on the `Add datasource` button.
|
||||
**Datasource manager** is on the left-sidebar of the app builder. To add a new data source, click on the `Add datasource` button.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
You will be prompted to select the datasource that you wish to add. Let's select PostgreSQL for this tutorial. You will then need to provide the credentials of your PostgreSQL database. The fields that are marked as `encrypted` will be encrypted before saving to ToolJet's database.
|
||||
You will be prompted to select the data source that you wish to add. Let's select PostgreSQL for this tutorial. You will then need to provide the credentials of your PostgreSQL database. The fields that are marked as `encrypted` will be encrypted before saving to ToolJet's database.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
The name of the datasource must be unique (within the app) and can be changed by clicking on the datasource name at the top of the prompt. Click on `Test Connection` button to verify the connection, this might take a couple of minutes. Once verified, save the datasource.
|
||||
The name of the data source must be unique (within the app) and can be changed by clicking on the data source name at the top of the prompt. Click on `Test Connection` button to verify the connection, this might take a couple of minutes. Once verified, save the data source.
|
||||
|
||||
:::tip
|
||||
If you are using ToolJet cloud and if your datasource is not publicly accessible, please white-list our IP address ( shown while creating a new datasource ).
|
||||
If you are using ToolJet cloud and if your data source is not publicly accessible, please white-list our IP address ( shown while creating a new data source ).
|
||||
:::
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
|
@ -5,7 +5,7 @@ sidebar_position: 1
|
|||
# Creating new app
|
||||
|
||||
:::info
|
||||
Apps in ToolJet binds the widgets, datasources and queries together.
|
||||
Apps in ToolJet binds the widgets, data sources and queries together.
|
||||
:::
|
||||
|
||||
This tutorial will walk you through building a simple app to fetch customer information from a PostgreSQL database and display the data using the table widget.
|
||||
|
|
@ -29,4 +29,4 @@ The main components of an app:
|
|||
|
||||
- **[Widgets](https://docs.tooljet.com/docs/tutorial/adding-widget)** - UI components such as tables, buttons, dropdowns.
|
||||
- **[Data sources](https://docs.tooljet.com/docs/tutorial/adding-a-datasource)** - ToolJet can connect to databases, APIs and external services to fetch and modify data.
|
||||
- **[Queries](https://docs.tooljet.com/docs/tutorial/building-queries)** - Queries are used to access the connected datasources.
|
||||
- **[Queries](https://docs.tooljet.com/docs/tutorial/building-queries)** - Queries are used to access the connected data sources.
|
||||
|
|
@ -5,7 +5,7 @@ sidebar_position: 13
|
|||
# Tracking
|
||||
|
||||
:::tip
|
||||
ToolJet does not store any data fetched from the datasources. ToolJet acts as a proxy and the data from datasources is sent to the client application without storing.
|
||||
ToolJet does not store any data fetched from the data sources. ToolJet acts as a proxy and the data from data sources is sent to the client application without storing.
|
||||
:::
|
||||
|
||||
## Server
|
||||
|
|
@ -16,6 +16,6 @@ Self-hosted version of ToolJet pings our server to fetch the latest product upda
|
|||
|
||||
## Client
|
||||
|
||||
ToolJet tracks anonymous usage data such as page loads and clicks. ToolJet tracks only the events and doesn't capture data from datasources.
|
||||
ToolJet tracks anonymous usage data such as page loads and clicks. ToolJet tracks only the events and doesn't capture data from data sources.
|
||||
|
||||
Tracking can be disabled by setting the value environment variable `ENABLE_TRACKING` to `0`.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ To let the user select one or more rows from the current page of a table, enable
|
|||
Activate this option on the inspector to have the last selected(clicked on) row to be highlighted.
|
||||
|
||||
## Search
|
||||
Client-side search is enabled by default and server-side search can be enabled from the events section of the inspector. Whenever the search text is changed, the `searchText` property of the table component is updated. If server-side search is enabled, `on search` event is fired after the content of `searchText` property is changed. `searchText` can be used to run a specific query to search for the records in your datasource.
|
||||
Client-side search is enabled by default and server-side search can be enabled from the events section of the inspector. Whenever the search text is changed, the `searchText` property of the table component is updated. If server-side search is enabled, `on search` event is fired after the content of `searchText` property is changed. `searchText` can be used to run a specific query to search for the records in your data source.
|
||||
|
||||
If you don't wish to use the search feature altogether, you can disable it from the inspector.
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ Along with `changeSet`, `dataUpdates` property will also be changed when the val
|
|||
}]
|
||||
```
|
||||
|
||||
If the data of a cell is changed, "save changes" button will be shown at the bottom of the table. This button when clicked will trigger the `Bulk update query` event. This event can be used to run a query to update the data on your datasource.
|
||||
If the data of a cell is changed, "save changes" button will be shown at the bottom of the table. This button when clicked will trigger the `Bulk update query` event. This event can be used to run a query to update the data on your data source.
|
||||
|
||||
#### Exposed variables
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ If the data of a cell is changed, "save changes" button will be shown at the bot
|
|||
| Loading state | Shows a loading status if the value is `true`. This property is often used with the `isLoading` property of queries so that the table shows a spinner while the query is being run. Default value is `false` .|
|
||||
| Server-side pagination | Server-side pagination can be used to run a query whenever the page is changed. If enabled, `pageIndex` property will be exposed on the table object, this property will have the current page index. |
|
||||
| Client-side pagination | Client-side pagination is enabled by default. The number of records per page is 10 by default and can be changed to upto 50. |
|
||||
| Server-side search | If server-side search is enabled, `on search` event is fired after the content of `searchText` property is changed. `searchText` can be used to run a specific query to search for the records in your datasource. |
|
||||
| Server-side search | If server-side search is enabled, `on search` event is fired after the content of `searchText` property is changed. `searchText` can be used to run a specific query to search for the records in your data source. |
|
||||
| Background color (Action Button) | Background color of the action button. |
|
||||
| Text color (Action Button) | Color of button-text of the action button. |
|
||||
| Show search box | It can be used to show or hide Table Search box. |
|
||||
|
|
|
|||
|
|
@ -123,6 +123,12 @@ body {
|
|||
article h2 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
.menu::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
.menu:hover::-webkit-scrollbar{
|
||||
width: 6.5px;
|
||||
}
|
||||
}
|
||||
|
||||
.menu__list {
|
||||
|
|
@ -135,11 +141,6 @@ body {
|
|||
width: 0.75rem;
|
||||
}
|
||||
|
||||
.menu::-webkit-scrollbar {
|
||||
width: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.hero--primary {
|
||||
--ifm-hero-background-color: #00bcd4;
|
||||
}
|
||||
|
|
|
|||
BIN
docs/static/img/datasource-reference/google-sheets/append-data-op.png
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/static/img/datasource-reference/google-sheets/delete-row-op.png
vendored
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/static/img/datasource-reference/google-sheets/get-info.png
vendored
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/static/img/datasource-reference/google-sheets/get-info2.png
vendored
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/static/img/datasource-reference/google-sheets/googlesheets.gif
vendored
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
docs/static/img/datasource-reference/google-sheets/read-data-op.png
vendored
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/static/img/datasource-reference/google-sheets/update-data-op.png
vendored
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/static/img/datasource-reference/googlesheet.gif
vendored
|
Before Width: | Height: | Size: 4.1 MiB |
BIN
docs/static/img/datasource-reference/mysql-query.png
vendored
|
Before Width: | Height: | Size: 52 KiB |
BIN
docs/static/img/datasource-reference/mysql/mysql-guimode.png
vendored
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
docs/static/img/datasource-reference/mysql/mysql-sqlmode.png
vendored
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
BIN
docs/static/img/datasource-reference/pg-connect.png
vendored
|
Before Width: | Height: | Size: 134 KiB |
BIN
docs/static/img/datasource-reference/pg-query.png
vendored
|
Before Width: | Height: | Size: 96 KiB |
BIN
docs/static/img/datasource-reference/postgresql/pg-gui.png
vendored
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/static/img/datasource-reference/postgresql/pg-sql.png
vendored
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/static/img/datasource-reference/postgresql/pgconnect.png
vendored
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
docs/static/img/datasource-reference/snowflake/snowflake-connect.png
vendored
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
docs/static/img/datasource-reference/snowflake/snowflake-query.png
vendored
Normal file
|
After Width: | Height: | Size: 59 KiB |
821
frontend/package-lock.json
generated
|
|
@ -9,8 +9,8 @@
|
|||
"@babel/preset-env": "^7.4.3",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@react-google-maps/api": "^2.1.1",
|
||||
"@sentry/react": "^6.12.0",
|
||||
"@sentry/tracing": "^6.12.0",
|
||||
"@sentry/react": "^6.17.6",
|
||||
"@sentry/tracing": "^6.17.6",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
|
|
@ -59,6 +59,7 @@
|
|||
"react-hot-toast": "^2.1.1",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-lazy-load-image-component": "^1.5.1",
|
||||
"react-lazyload": "^3.2.0",
|
||||
"react-loading-skeleton": "^2.2.0",
|
||||
"react-mentions": "^4.3.0",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import { renderTooltip } from '../_helpers/appUtils';
|
|||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import '@/_styles/custom.scss';
|
||||
import { resolveProperties, resolveStyles } from './component-properties-resolution';
|
||||
import { validateWidget } from '@/_helpers/utils';
|
||||
import { validateWidget, resolveReferences } from '@/_helpers/utils';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
|
||||
const AllComponents = {
|
||||
|
|
@ -123,10 +123,11 @@ export const Box = function Box({
|
|||
resolvedStyles.visibility = resolvedStyles.visibility !== false ? true : false;
|
||||
|
||||
let exposedVariables = {};
|
||||
let isListView = false;
|
||||
|
||||
if (component.parent) {
|
||||
const parentComponent = allComponents[component.parent];
|
||||
const isListView = parentComponent?.component?.component === 'Listview';
|
||||
isListView = parentComponent?.component?.component === 'Listview';
|
||||
|
||||
if (isListView) {
|
||||
const itemsAtIndex = currentState?.components[parentId]?.data[extraProps.listviewItemIndex];
|
||||
|
|
@ -142,7 +143,12 @@ export const Box = function Box({
|
|||
if (mode === 'edit' && eventName === 'onClick') {
|
||||
onComponentClick(id, component);
|
||||
}
|
||||
onEvent(eventName, { ...options, component });
|
||||
const listItem = isListView
|
||||
? resolveReferences(allComponents[component.parent].component.definition.properties.data.value, currentState)[
|
||||
extraProps.listviewItemIndex
|
||||
] ?? {}
|
||||
: {};
|
||||
onEvent(eventName, { ...options, customVariables: { listItem }, component });
|
||||
};
|
||||
const validate = (value) =>
|
||||
validateWidget({
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import 'codemirror/theme/base16-light.css';
|
|||
import 'codemirror/theme/duotone-light.css';
|
||||
import 'codemirror/theme/monokai.css';
|
||||
import { getSuggestionKeys, onBeforeChange, handleChange } from './utils';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { resolveReferences, hasCircularDependency, handleCircularStructureToJSON } from '@/_helpers/utils';
|
||||
import useHeight from '@/_hooks/use-height-transition';
|
||||
import usePortal from '@/_hooks/use-portal';
|
||||
import { Color } from './Elements/Color';
|
||||
|
|
@ -129,8 +129,14 @@ export function CodeHinter({
|
|||
);
|
||||
}
|
||||
|
||||
const previewType = typeof preview;
|
||||
const content = getPreviewContent(preview, previewType);
|
||||
let previewType = typeof preview;
|
||||
let previewContent = preview;
|
||||
|
||||
if (hasCircularDependency(preview)) {
|
||||
previewContent = JSON.stringify(preview, handleCircularStructureToJSON());
|
||||
previewType = typeof previewContent;
|
||||
}
|
||||
const content = getPreviewContent(previewContent, previewType);
|
||||
|
||||
return (
|
||||
<animated.div className={isOpen ? themeCls : null} style={{ ...slideInStyles, overflow: 'hidden' }}>
|
||||
|
|
@ -184,7 +190,7 @@ export function CodeHinter({
|
|||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${(height === '150px' || height === '300px') && 'tablr-gutter-x-0'} row`}
|
||||
className={`row${height === '150px' || height === '300px' ? ' tablr-gutter-x-0' : ''}`}
|
||||
style={{ width: width, display: codeShow ? 'flex' : 'none' }}
|
||||
>
|
||||
<div className={`col`} style={{ marginBottom: '0.5rem' }}>
|
||||
|
|
@ -216,7 +222,7 @@ export function CodeHinter({
|
|||
value={typeof initialValue === 'string' ? initialValue : ''}
|
||||
realState={realState}
|
||||
scrollbarStyle={null}
|
||||
height={height || 'auto'}
|
||||
height={'100%'}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={(editor) => {
|
||||
const value = editor.getValue();
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ export const Listview = function Listview({
|
|||
>
|
||||
<SubContainer
|
||||
parentComponent={component}
|
||||
readOnly={index !== 0}
|
||||
containerCanvasWidth={width}
|
||||
parent={`${id}`}
|
||||
parentName={component.name}
|
||||
{...containerProps}
|
||||
readOnly={index !== 0}
|
||||
customResolvables={{ listItem }}
|
||||
parentRef={parentRef}
|
||||
removeComponent={removeComponent}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useTrail } from 'react-spring';
|
|||
|
||||
import Star from './star';
|
||||
|
||||
export const StarRating = function StarRating({ properties, styles, fireEvent, setExposedVariable }) {
|
||||
export const StarRating = function StarRating({ properties, styles, fireEvent, setExposedVariable, darkMode }) {
|
||||
const label = properties.label;
|
||||
const defaultSelected = properties.defaultSelected ?? 5;
|
||||
const maxRating = properties.maxRating ?? 5;
|
||||
|
|
@ -14,6 +14,7 @@ export const StarRating = function StarRating({ properties, styles, fireEvent, s
|
|||
|
||||
const { visibility, disabledState, textColor, labelColor } = styles;
|
||||
const color = textColor ?? '#ffb400';
|
||||
const labelColorStyle = labelColor === '#333' ? (darkMode ? '#fff' : '#333') : labelColor;
|
||||
|
||||
const animatedStars = useTrail(maxRating, {
|
||||
config: {
|
||||
|
|
@ -68,7 +69,7 @@ export const StarRating = function StarRating({ properties, styles, fireEvent, s
|
|||
|
||||
return (
|
||||
<div data-disabled={disabledState} className="star-rating" style={{ display: visibility ? '' : 'none' }}>
|
||||
<span className="label form-check-label col-auto" style={{ color: labelColor }}>
|
||||
<span className="label form-check-label col-auto" style={{ color: labelColorStyle }}>
|
||||
{label}
|
||||
</span>
|
||||
<div className="col px-1 py-0 mt-0">
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
export const Text = function Text({ height, properties, styles }) {
|
||||
export const Text = function Text({ height, properties, styles, darkMode }) {
|
||||
const [loadingState, setLoadingState] = useState(false);
|
||||
|
||||
const { textColor, textAlign, visibility, disabledState } = styles;
|
||||
const text = properties.text ?? '';
|
||||
const color = textColor;
|
||||
const color = textColor === '#000' ? (darkMode ? '#fff' : '#000') : textColor;
|
||||
|
||||
useEffect(() => {
|
||||
const loadingStateProperty = properties.loadingState;
|
||||
|
|
|
|||
|
|
@ -580,7 +580,7 @@ class DataSourceManager extends React.Component {
|
|||
</svg>
|
||||
</div>
|
||||
<div className="col" style={{ maxWidth: '480px' }}>
|
||||
<p>Please white-list our IP address if your databases are not publicly accessabile</p>
|
||||
<p>Please white-list our IP address if the data source is not publicly accessible.</p>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
{isCopied ? (
|
||||
|
|
|
|||
|
|
@ -72,7 +72,10 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
}
|
||||
|
||||
this.setState({
|
||||
options: paneHeightChanged ? this.state.options : selectedQuery.options,
|
||||
options:
|
||||
paneHeightChanged || this.state.selectedQuery?.id === selectedQuery?.id
|
||||
? this.state.options
|
||||
: selectedQuery.options,
|
||||
selectedDataSource: source,
|
||||
selectedQuery,
|
||||
queryName: selectedQuery.name,
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ class Viewer extends React.Component {
|
|||
this.setStateForApp(data);
|
||||
this.setStateForContainer(data);
|
||||
this.setState({ isLoading: false });
|
||||
this.setWindowTitle(data.name);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -155,6 +156,10 @@ class Viewer extends React.Component {
|
|||
return canvasBoundingRect?.width;
|
||||
};
|
||||
|
||||
setWindowTitle(name) {
|
||||
document.title = name ?? 'Untitled App';
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
appDefinition,
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ export const WidgetManager = function WidgetManager({ componentTypes, zoomLevel,
|
|||
return (
|
||||
<>
|
||||
{renderList(commonSection.title, commonSection.items)}
|
||||
{renderList(formSection.title, formSection.items)}
|
||||
{renderList(layoutsSection.title, layoutsSection.items)}
|
||||
{renderList(formSection.title, formSection.items)}
|
||||
{renderList(otherSection.title, otherSection.items)}
|
||||
{renderList(integrationSection.title, integrationSection.items)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,13 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Container, Row, Badge } from 'react-bootstrap';
|
||||
import LazyLoad from 'react-lazyload';
|
||||
import { ImageWithSpinner } from '@/_components';
|
||||
import { getSvgIcon } from '@/_helpers/appUtils';
|
||||
|
||||
export default function TemplateDisplay(props) {
|
||||
const { id, name, description, sources } = props?.app ?? {};
|
||||
const componentRef = useRef(null);
|
||||
const [isloaded, setLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (componentRef.current) {
|
||||
setLoaded(true);
|
||||
}
|
||||
}, [componentRef]);
|
||||
|
||||
return (
|
||||
<div className="template-display" ref={componentRef}>
|
||||
<div className="template-display">
|
||||
<Container fluid className="pt-2">
|
||||
<Row style={{ height: '10%' }}>
|
||||
<h3 className="title">{name}</h3>
|
||||
|
|
@ -48,15 +40,13 @@ export default function TemplateDisplay(props) {
|
|||
))}
|
||||
</span>
|
||||
</Row>
|
||||
<Row className="align-items-center justify-content-center" style={{ height: '88%' }}>
|
||||
{isloaded && (
|
||||
<LazyLoad>
|
||||
<img
|
||||
className="template-image"
|
||||
src={`/assets/images/templates/${id}${props.darkMode ? '-dark' : ''}.png`}
|
||||
/>
|
||||
</LazyLoad>
|
||||
)}
|
||||
<Row className="align-items-center justify-content-center" style={{ height: '88%', position: 'relative' }}>
|
||||
<ImageWithSpinner
|
||||
src={`/assets/images/templates/${id}${props.darkMode ? '-dark' : ''}.png`}
|
||||
className="template-image"
|
||||
spinnerClassName="template-spinner"
|
||||
useSmallSpinner={true}
|
||||
/>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -55,40 +55,63 @@ class OnboardingModal extends React.Component {
|
|||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title className="text-center">Finish ToolJet installation</Modal.Title>
|
||||
<br />
|
||||
</Modal.Header>
|
||||
|
||||
<Modal.Body>
|
||||
<h3>Receive product updates from the ToolJet team? </h3>
|
||||
<small>We hate spam and we will never spam</small>
|
||||
<div className="mb-3 mt-2">
|
||||
<label className="form-label">Organization</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
onChange={(e) => {
|
||||
this.changeOptions('org', e.target.value);
|
||||
}}
|
||||
/>
|
||||
<span className="input-group-text"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mt-3"
|
||||
placeholder={'Your name'}
|
||||
onChange={(e) => {
|
||||
this.changeOptions('name', e.target.value);
|
||||
}}
|
||||
/>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Name</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
onChange={(e) => {
|
||||
this.changeOptions('name', e.target.value);
|
||||
}}
|
||||
/>
|
||||
<span className="input-group-text"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mt-3"
|
||||
placeholder={'Your email'}
|
||||
onChange={(e) => {
|
||||
this.changeOptions('email', e.target.value);
|
||||
}}
|
||||
/>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Email</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
onChange={(e) => {
|
||||
this.changeOptions('email', e.target.value);
|
||||
}}
|
||||
/>
|
||||
<span className="input-group-text"></span>
|
||||
</div>
|
||||
</div>
|
||||
<small>You will receive updates from the ToolJet team ( 1-2 emails every month, we do not spam )</small>
|
||||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
<div className="row w-100">
|
||||
<div className="row w-100 gx-0">
|
||||
<div className="col">
|
||||
<button className={`btn btn-primary`} onClick={this.finishOnboarding}>
|
||||
Finish setup
|
||||
</button>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<a onClick={this.skipOnboard} className="mt-2">
|
||||
<a onClick={this.skipOnboard} className="mt-3 text-muted">
|
||||
Skip
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
32
frontend/src/_components/ImageWithSpinner.jsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* Props
|
||||
src = source
|
||||
className = custom css class for lazyload wrapper
|
||||
spinnerClassName = custom css class for spinner
|
||||
useSmallSpinner = boolean value to change default spinner to smaller spinner [default: false]
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { LazyLoadImage } from 'react-lazy-load-image-component';
|
||||
|
||||
export const ImageWithSpinner = ({ src, className, spinnerClassName, useSmallSpinner }) => {
|
||||
const [isloaded, setLoaded] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LazyLoadImage
|
||||
src={src}
|
||||
className={className}
|
||||
beforeLoad={() => setLoaded(false)}
|
||||
afterLoad={() => setLoaded(true)}
|
||||
/>
|
||||
{!isloaded && (
|
||||
<div
|
||||
className={`spinner-border text-center ${spinnerClassName ?? ''} ${
|
||||
useSmallSpinner ? 'spinner-border-sm' : ''
|
||||
}`}
|
||||
role="status"
|
||||
></div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -5,3 +5,4 @@ export * from './ConfirmDialog';
|
|||
export * from './DarkModeToggle';
|
||||
export * from './SearchBox';
|
||||
export * from './ToolTip';
|
||||
export * from './ImageWithSpinner';
|
||||
|
|
|
|||
|
|
@ -87,12 +87,12 @@ export function runTransformation(_ref, rawData, transformation, query) {
|
|||
return result;
|
||||
}
|
||||
|
||||
export async function executeActionsForEventId(_ref, eventId, component, mode) {
|
||||
export async function executeActionsForEventId(_ref, eventId, component, mode, customVariables) {
|
||||
const events = component.definition.events || [];
|
||||
const filteredEvents = events.filter((event) => event.eventId === eventId);
|
||||
|
||||
for (const event of filteredEvents) {
|
||||
await executeAction(_ref, event, mode); // skipcq: JS-0032
|
||||
await executeAction(_ref, event, mode, customVariables); // skipcq: JS-0032
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,11 +149,12 @@ function showModal(_ref, modal, show) {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function executeAction(_ref, event, mode) {
|
||||
function executeAction(_ref, event, mode, customVariables) {
|
||||
console.log('nopski', customVariables);
|
||||
if (event) {
|
||||
switch (event.actionId) {
|
||||
case 'show-alert': {
|
||||
const message = resolveReferences(event.message, _ref.state.currentState);
|
||||
const message = resolveReferences(event.message, _ref.state.currentState, undefined, customVariables);
|
||||
switch (event.alertType) {
|
||||
case 'success':
|
||||
case 'error':
|
||||
|
|
@ -177,20 +178,22 @@ function executeAction(_ref, event, mode) {
|
|||
}
|
||||
|
||||
case 'open-webpage': {
|
||||
const url = resolveReferences(event.url, _ref.state.currentState);
|
||||
const url = resolveReferences(event.url, _ref.state.currentState, undefined, customVariables);
|
||||
window.open(url, '_blank');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
case 'go-to-app': {
|
||||
const slug = resolveReferences(event.slug, _ref.state.currentState);
|
||||
const slug = resolveReferences(event.slug, _ref.state.currentState, undefined, customVariables);
|
||||
const queryParams = event.queryParams?.reduce(
|
||||
(result, queryParam) => ({
|
||||
...result,
|
||||
...{
|
||||
[resolveReferences(queryParam[0], _ref.state.currentState)]: resolveReferences(
|
||||
queryParam[1],
|
||||
_ref.state.currentState
|
||||
_ref.state.currentState,
|
||||
undefined,
|
||||
customVariables
|
||||
),
|
||||
},
|
||||
}),
|
||||
|
|
@ -223,15 +226,20 @@ function executeAction(_ref, event, mode) {
|
|||
return showModal(_ref, event.modal, false);
|
||||
|
||||
case 'copy-to-clipboard': {
|
||||
const contentToCopy = resolveReferences(event.contentToCopy, _ref.state.currentState);
|
||||
const contentToCopy = resolveReferences(
|
||||
event.contentToCopy,
|
||||
_ref.state.currentState,
|
||||
undefined,
|
||||
customVariables
|
||||
);
|
||||
copyToClipboard(contentToCopy);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
case 'set-localstorage-value': {
|
||||
const key = resolveReferences(event.key, _ref.state.currentState);
|
||||
const value = resolveReferences(event.value, _ref.state.currentState);
|
||||
const key = resolveReferences(event.key, _ref.state.currentState, undefined, customVariables);
|
||||
const value = resolveReferences(event.value, _ref.state.currentState, undefined, customVariables);
|
||||
localStorage.setItem(key, value);
|
||||
|
||||
return Promise.resolve();
|
||||
|
|
@ -239,8 +247,9 @@ function executeAction(_ref, event, mode) {
|
|||
|
||||
case 'generate-file': {
|
||||
// const fileType = event.fileType;
|
||||
const data = resolveReferences(event.data, _ref.state.currentState) ?? [];
|
||||
const fileName = resolveReferences(event.fileName, _ref.state.currentState) ?? 'data.txt';
|
||||
const data = resolveReferences(event.data, _ref.state.currentState, undefined, customVariables) ?? [];
|
||||
const fileName =
|
||||
resolveReferences(event.fileName, _ref.state.currentState, undefined, customVariables) ?? 'data.txt';
|
||||
|
||||
const csv = generateCSV(data);
|
||||
generateFile(fileName, csv);
|
||||
|
|
@ -253,8 +262,8 @@ function executeAction(_ref, event, mode) {
|
|||
}
|
||||
|
||||
case 'set-custom-variable': {
|
||||
const key = resolveReferences(event.key, _ref.state.currentState);
|
||||
const value = resolveReferences(event.value, _ref.state.currentState);
|
||||
const key = resolveReferences(event.key, _ref.state.currentState, undefined, customVariables);
|
||||
const value = resolveReferences(event.value, _ref.state.currentState, undefined, customVariables);
|
||||
const customVariables = { ..._ref.state.currentState.variables };
|
||||
customVariables[key] = value;
|
||||
|
||||
|
|
@ -267,7 +276,7 @@ function executeAction(_ref, event, mode) {
|
|||
}
|
||||
|
||||
case 'unset-custom-variable': {
|
||||
const key = resolveReferences(event.key, _ref.state.currentState);
|
||||
const key = resolveReferences(event.key, _ref.state.currentState, undefined, customVariables);
|
||||
const customVariables = { ..._ref.state.currentState.variables };
|
||||
delete customVariables[key];
|
||||
|
||||
|
|
@ -286,6 +295,8 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') {
|
|||
let _self = _ref;
|
||||
console.log('Event: ', eventName);
|
||||
|
||||
const { customVariables } = options;
|
||||
|
||||
if (eventName === 'onRowClicked') {
|
||||
const { component, data, rowId } = options;
|
||||
_self.setState(
|
||||
|
|
@ -303,7 +314,7 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') {
|
|||
},
|
||||
},
|
||||
() => {
|
||||
executeActionsForEventId(_ref, 'onRowClicked', component, mode);
|
||||
executeActionsForEventId(_ref, 'onRowClicked', component, mode, customVariables);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -324,7 +335,7 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') {
|
|||
},
|
||||
},
|
||||
() => {
|
||||
executeActionsForEventId(_ref, 'onCalendarEventSelect', component, mode);
|
||||
executeActionsForEventId(_ref, 'onCalendarEventSelect', component, mode, customVariables);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -345,7 +356,7 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') {
|
|||
},
|
||||
},
|
||||
() => {
|
||||
executeActionsForEventId(_ref, 'onCalendarSlotSelect', component, mode);
|
||||
executeActionsForEventId(_ref, 'onCalendarSlotSelect', component, mode, customVariables);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -371,7 +382,7 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') {
|
|||
for (const event of action.events) {
|
||||
if (event.actionId) {
|
||||
// the event param uses a hacky workaround for using same format used by event manager ( multiple handlers )
|
||||
await executeAction(_self, { ...event, ...event.options }, mode);
|
||||
await executeAction(_self, { ...event, ...event.options }, mode, customVariables);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -402,7 +413,7 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') {
|
|||
for (const event of column.events) {
|
||||
if (event.actionId) {
|
||||
// the event param uses a hacky workaround for using same format used by event manager ( multiple handlers )
|
||||
await executeAction(_self, { ...event, ...event.options }, mode);
|
||||
await executeAction(_self, { ...event, ...event.options }, mode, customVariables);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -439,17 +450,17 @@ export async function onEvent(_ref, eventName, options, mode = 'edit') {
|
|||
].includes(eventName)
|
||||
) {
|
||||
const { component } = options;
|
||||
executeActionsForEventId(_ref, eventName, component, mode);
|
||||
executeActionsForEventId(_ref, eventName, component, mode, customVariables);
|
||||
}
|
||||
|
||||
if (eventName === 'onBulkUpdate') {
|
||||
onComponentOptionChanged(_self, options.component, 'isSavingChanges', true);
|
||||
await executeActionsForEventId(_self, eventName, options.component, mode);
|
||||
await executeActionsForEventId(_self, eventName, options.component, mode, customVariables);
|
||||
onComponentOptionChanged(_self, options.component, 'isSavingChanges', false);
|
||||
}
|
||||
|
||||
if (['onDataQuerySuccess', 'onDataQueryFailure'].includes(eventName)) {
|
||||
await executeActionsForEventId(_self, eventName, options, mode);
|
||||
await executeActionsForEventId(_self, eventName, options, mode, customVariables);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ export function resolve(data, state) {
|
|||
}
|
||||
|
||||
export function resolveReferences(object, state, defaultValue, customObjects = {}, withError = false) {
|
||||
const reservedKeyword = ['app']; //Keywords that slows down the app
|
||||
object = _.clone(object);
|
||||
|
||||
const objectType = typeof object;
|
||||
let error;
|
||||
switch (objectType) {
|
||||
|
|
@ -45,9 +45,23 @@ export function resolveReferences(object, state, defaultValue, customObjects = {
|
|||
const code = object.replace('{{', '').replace('}}', '');
|
||||
let result = '';
|
||||
|
||||
if (reservedKeyword.includes(code)) {
|
||||
error = `${code} is a reserved keyword`;
|
||||
return [{}, error];
|
||||
}
|
||||
|
||||
try {
|
||||
const evalFunction = Function(
|
||||
['variables', 'components', 'queries', 'globals', 'moment', '_', ...Object.keys(customObjects)],
|
||||
[
|
||||
'variables',
|
||||
'components',
|
||||
'queries',
|
||||
'globals',
|
||||
'moment',
|
||||
'_',
|
||||
...Object.keys(customObjects),
|
||||
reservedKeyword,
|
||||
],
|
||||
`return ${code}`
|
||||
);
|
||||
result = evalFunction(
|
||||
|
|
@ -57,13 +71,13 @@ export function resolveReferences(object, state, defaultValue, customObjects = {
|
|||
state.globals,
|
||||
moment,
|
||||
_,
|
||||
...Object.values(customObjects)
|
||||
...Object.values(customObjects),
|
||||
null
|
||||
);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
console.log('eval_error', err);
|
||||
}
|
||||
|
||||
if (withError) return [result, error];
|
||||
return result;
|
||||
}
|
||||
|
|
@ -300,3 +314,26 @@ export const isJson = (str) => {
|
|||
export function buildURLWithQuery(url, query = {}) {
|
||||
return `${url}?${toQuery(query)}`;
|
||||
}
|
||||
|
||||
export const handleCircularStructureToJSON = () => {
|
||||
const seen = new WeakSet();
|
||||
|
||||
return (key, value) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.has(value)) {
|
||||
return 'Object';
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
export function hasCircularDependency(obj) {
|
||||
try {
|
||||
JSON.stringify(obj);
|
||||
} catch (e) {
|
||||
return String(e).includes('Converting circular structure to JSON');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@
|
|||
cursor: pointer;
|
||||
margin-bottom: 4px;
|
||||
margin-left: 14px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
padding: 9px 12px !important;
|
||||
|
|
|
|||
|
|
@ -920,6 +920,17 @@ button {
|
|||
object-fit: contain;
|
||||
}
|
||||
|
||||
.template-spinner{
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
|
@ -2245,6 +2256,8 @@ body {
|
|||
|
||||
.CodeMirror-scroll {
|
||||
overflow: hidden !important;
|
||||
position: static;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3226,7 +3239,6 @@ input[type="text"] {
|
|||
min-height: 26px;
|
||||
padding: 0;
|
||||
padding-left: 2px;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
td.rdtActive,
|
||||
|
|
@ -3975,6 +3987,7 @@ input[type="text"] {
|
|||
|
||||
.form-control {
|
||||
border-color: $border-grey-dark !important;
|
||||
color: inherit;
|
||||
}
|
||||
input {
|
||||
background-color: $bg-dark-light !important;
|
||||
|
|
@ -4256,20 +4269,19 @@ input[type="text"] {
|
|||
margin-top: 0.65rem !important;
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line {
|
||||
height: 36px !important;
|
||||
height: 32px !important;
|
||||
margin-bottom: 6px !important;
|
||||
}
|
||||
.CodeMirror-placeholder {
|
||||
height: 36px !important;
|
||||
height: 21px !important;
|
||||
position: absolute !important;
|
||||
margin-top: 0px !important;
|
||||
margin-top: 3px !important;
|
||||
}
|
||||
.CodeMirror-cursor {
|
||||
height: inherit !important;
|
||||
}
|
||||
.CodeMirror-lines {
|
||||
margin-top: 6px !important;
|
||||
height: 36px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
'@ee': path.resolve(__dirname, 'ee/'),
|
||||
},
|
||||
},
|
||||
...(environment === 'development' && { devtool: 'inline-source-map' }),
|
||||
devtool: environment === 'development' ? 'inline-source-map' : 'source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
|
|
|
|||
1156
plugins/package-lock.json
generated
|
|
@ -34,6 +34,7 @@
|
|||
"@tooljet-plugins/s3": "file:packages/s3",
|
||||
"@tooljet-plugins/sendgrid": "file:packages/sendgrid",
|
||||
"@tooljet-plugins/slack": "file:packages/slack",
|
||||
"@tooljet-plugins/snowflake": "file:packages/snowflake",
|
||||
"@tooljet-plugins/stripe": "file:packages/stripe",
|
||||
"@tooljet-plugins/twilio": "file:packages/twilio",
|
||||
"@tooljet-plugins/typesense": "file:packages/typesense"
|
||||
|
|
|
|||
4
plugins/packages/snowflake/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
lib/*.d.*
|
||||
lib/*.js
|
||||
lib/*.js.map
|
||||
4
plugins/packages/snowflake/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
# Snowflake
|
||||
|
||||
Documentation on: https://docs.tooljet.com/docs/data-sources/snowflake
|
||||
7
plugins/packages/snowflake/__tests_/index.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const snowflake = require('../lib');
|
||||
|
||||
describe('snowflake', () => {
|
||||
it.todo('needs tests');
|
||||
});
|
||||
13
plugins/packages/snowflake/lib/icon.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<svg viewBox="0 0 44 44" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Snowflake_Logo" transform="translate(-0.002027, 0.531250)" fill="#29B5E8" fill-rule="nonzero">
|
||||
<path d="M37.2637465,33.128906 L28.0879655,27.828125 C26.7989025,27.085938 25.1504655,27.527344 24.4043715,28.816406 C24.1153085,29.324219 24.0020275,29.882812 24.0567155,30.425781 L24.0567155,40.785156 C24.0567155,42.265625 25.2598395,43.46875 26.7442155,43.46875 C28.2246835,43.46875 29.4278085,42.265625 29.4278085,40.785156 L29.4278085,34.828125 L34.5684335,37.796875 C35.8574965,38.542969 37.5098395,38.097656 38.2520275,36.808594 C38.9981215,35.519531 38.5567155,33.871094 37.2637465,33.128906" id="Path"></path>
|
||||
<path d="M14.4434335,21.769531 C14.4590585,20.8125 13.9551525,19.921875 13.1270275,19.441406 L3.95124649,14.144531 C3.55280849,13.914062 3.09577749,13.792969 2.63874649,13.792969 C1.69733949,13.792969 0.822339495,14.296875 0.353589495,15.109375 C-0.372972505,16.367188 0.060621495,17.980469 1.31843349,18.707031 L6.60749649,21.757812 L1.31843349,24.8125 C0.709058495,25.164062 0.271558495,25.730469 0.091871495,26.410156 C-0.091722505,27.089844 0.00202749493,27.800781 0.353589495,28.410156 C0.822339495,29.222656 1.69733949,29.726562 2.63483949,29.726562 C3.09577749,29.726562 3.55280849,29.605469 3.95124649,29.375 L13.1270275,24.078125 C13.9473395,23.601562 14.4512465,22.71875 14.4434335,21.769531" id="Path2"></path>
|
||||
<path d="M6.03327749,10.390625 L15.2090585,15.6875 C16.2793715,16.308594 17.5996835,16.105469 18.4434335,15.28125 C18.9785895,14.789062 19.3106215,14.085938 19.3106215,13.304688 L19.3106215,2.6875 C19.3106215,1.203125 18.1074965,5.68434189e-14 16.6270275,5.68434189e-14 C15.1426525,5.68434189e-14 13.9395275,1.203125 13.9395275,2.6875 L13.9395275,8.730469 L8.72858949,5.722656 C7.43952749,4.976562 5.79108949,5.417969 5.04499649,6.707031 C4.29890249,7.996094 4.74421549,9.644531 6.03327749,10.390625" id="Path3"></path>
|
||||
<path d="M26.6660895,22.199219 C26.6660895,22.402344 26.5489025,22.683594 26.4043715,22.832031 L22.7676525,26.46875 C22.6231215,26.613281 22.3379655,26.730469 22.1348395,26.730469 L21.2090585,26.730469 C21.0059335,26.730469 20.7207775,26.613281 20.5762465,26.46875 L16.9356215,22.832031 C16.7910895,22.683594 16.6739025,22.402344 16.6739025,22.199219 L16.6739025,21.273438 C16.6739025,21.066406 16.7910895,20.785156 16.9356215,20.640625 L20.5762465,17 C20.7207775,16.855469 21.0059335,16.738281 21.2090585,16.738281 L22.1348395,16.738281 C22.3379655,16.738281 22.6231215,16.855469 22.7676525,17 L26.4043715,20.640625 C26.5489025,20.785156 26.6660895,21.066406 26.6660895,21.273438 L26.6660895,22.199219 Z M23.4199965,21.753906 L23.4199965,21.714844 C23.4199965,21.566406 23.3340585,21.359375 23.2285895,21.25 L22.1543715,20.179688 C22.0489025,20.070312 21.8418715,19.984375 21.6895275,19.984375 L21.6504655,19.984375 C21.5020275,19.984375 21.2949965,20.070312 21.1856215,20.179688 L20.1153085,21.25 C20.0098395,21.355469 19.9239025,21.5625 19.9239025,21.714844 L19.9239025,21.753906 C19.9239025,21.90625 20.0098395,22.113281 20.1153085,22.21875 L21.1856215,23.292969 C21.2949965,23.398438 21.5020275,23.484375 21.6504655,23.484375 L21.6895275,23.484375 C21.8418715,23.484375 22.0489025,23.398438 22.1543715,23.292969 L23.2285895,22.21875 C23.3340585,22.113281 23.4199965,21.90625 23.4199965,21.753906 Z" id="Combined-Shape"></path>
|
||||
<path d="M28.0879655,15.6875 L37.2637465,10.390625 C38.5528085,9.648438 38.9981215,7.996094 38.2520275,6.707031 C37.5059335,5.417969 35.8574965,4.976562 34.5684335,5.722656 L29.4278085,8.691406 L29.4278085,2.6875 C29.4278085,1.203125 28.2246835,0 26.7442155,0 C25.2598395,0 24.0567155,1.203125 24.0567155,2.6875 L24.0567155,13.09375 C24.0059335,13.632812 24.1114025,14.195312 24.4043715,14.703125 C25.1504655,15.992188 26.7989025,16.433594 28.0879655,15.6875" id="Path4"></path>
|
||||
<path d="M17.0489025,27.515625 C16.4395275,27.398438 15.7871835,27.496094 15.2090585,27.828125 L6.03327749,33.128906 C4.74421549,33.871094 4.29890249,35.519531 5.04499649,36.808594 C5.79108949,38.101562 7.43952749,38.542969 8.72858949,37.796875 L13.9395275,34.789062 L13.9395275,40.785156 C13.9395275,42.265625 15.1426525,43.46875 16.6270275,43.46875 C18.1074965,43.46875 19.3106215,42.265625 19.3106215,40.785156 L19.3106215,30.167969 C19.3106215,28.828125 18.3301525,27.71875 17.0489025,27.515625" id="Path5"></path>
|
||||
<path d="M42.9981215,15.078125 C42.2559335,13.785156 40.6035895,13.34375 39.3145275,14.089844 L30.1387465,19.386719 C29.2598395,19.894531 28.7754655,20.824219 28.7910895,21.769531 C28.7832775,22.710938 29.2676525,23.628906 30.1387465,24.128906 L39.3145275,29.429688 C40.6035895,30.171875 42.2520275,29.730469 42.9981215,28.441406 C43.7442155,27.152344 43.2989025,25.503906 42.0098395,24.757812 L36.8145275,21.757812 L42.0098395,18.757812 C43.3028085,18.015625 43.7442155,16.367188 42.9981215,15.078125" id="Path6"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5 KiB |
105
plugins/packages/snowflake/lib/index.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import {
|
||||
QueryError,
|
||||
QueryResult,
|
||||
QueryService,
|
||||
ConnectionTestResult,
|
||||
cacheConnection,
|
||||
getCachedConnection,
|
||||
} from "@tooljet-plugins/common";
|
||||
import { SourceOptions, QueryOptions } from "./types";
|
||||
import * as snowflake from "snowflake-sdk";
|
||||
|
||||
export default class Snowflake implements QueryService {
|
||||
async connExecuteAsync(connection: snowflake.Connection, options: any) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.execute({
|
||||
...options,
|
||||
complete: function (err, stmt, rows) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({ stmt, rows });
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async run(
|
||||
sourceOptions: SourceOptions,
|
||||
queryOptions: QueryOptions,
|
||||
dataSourceId: string,
|
||||
dataSourceUpdatedAt: string
|
||||
): Promise<QueryResult> {
|
||||
const sqlText = queryOptions.query;
|
||||
const connection: snowflake.Connection = await this.getConnection(
|
||||
sourceOptions,
|
||||
{},
|
||||
true,
|
||||
dataSourceId,
|
||||
dataSourceUpdatedAt
|
||||
);
|
||||
|
||||
try {
|
||||
const result: any = await this.connExecuteAsync(connection, {
|
||||
sqlText,
|
||||
});
|
||||
|
||||
return { status: "ok", data: result.rows };
|
||||
} catch (err) {
|
||||
throw new QueryError("Query could not be completed", err.message, {});
|
||||
}
|
||||
}
|
||||
|
||||
async testConnection(sourceOptions: SourceOptions): Promise<ConnectionTestResult> {
|
||||
await this.getConnection(sourceOptions, {}, false);
|
||||
|
||||
return { status: "ok" };
|
||||
}
|
||||
|
||||
async connAsync(connection: snowflake.Connection) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.connect(
|
||||
function(err, conn) {
|
||||
if (err) reject(err);
|
||||
resolve(conn);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async buildConnection(sourceOptions: SourceOptions) {
|
||||
let connection = snowflake.createConnection({
|
||||
account: sourceOptions.account,
|
||||
username: sourceOptions.username,
|
||||
password: sourceOptions.password,
|
||||
});
|
||||
|
||||
return await this.connAsync(connection)
|
||||
}
|
||||
|
||||
async getConnection(
|
||||
sourceOptions: any,
|
||||
options: any,
|
||||
checkCache: boolean,
|
||||
dataSourceId?: string,
|
||||
dataSourceUpdatedAt?: string
|
||||
): Promise<any> {
|
||||
if (checkCache) {
|
||||
let connection = await getCachedConnection(
|
||||
dataSourceId,
|
||||
dataSourceUpdatedAt
|
||||
);
|
||||
|
||||
if (connection) {
|
||||
return connection;
|
||||
} else {
|
||||
connection = await this.buildConnection(sourceOptions);
|
||||
await cacheConnection(dataSourceId, connection);
|
||||
return connection;
|
||||
}
|
||||
} else {
|
||||
return await this.buildConnection(sourceOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
plugins/packages/snowflake/lib/manifest.json
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/manifest.schema.json",
|
||||
"title": "Snowflake datasource",
|
||||
"description": "A schema defining Snowflake datasource",
|
||||
"type": "database",
|
||||
"source": {
|
||||
"name": "Snowflake",
|
||||
"kind": "snowflake",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"username": { "type": "string" },
|
||||
"account": { "type": "string" },
|
||||
"password": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"username": {
|
||||
"label": "Username",
|
||||
"key": "username",
|
||||
"type": "text",
|
||||
"description": "Enter username"
|
||||
},
|
||||
"account": {
|
||||
"label": "Account",
|
||||
"key": "account",
|
||||
"type": "text",
|
||||
"description": "Enter account"
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"key": "password",
|
||||
"type": "password",
|
||||
"description": "Enter password"
|
||||
},
|
||||
"database": {
|
||||
"label": "Database",
|
||||
"key": "database",
|
||||
"type": "text",
|
||||
"description": "Enter database"
|
||||
},
|
||||
"schema": {
|
||||
"label": "Schema",
|
||||
"key": "schema",
|
||||
"type": "text",
|
||||
"description": "Enter schema"
|
||||
},
|
||||
"warehouse": {
|
||||
"label": "Warehouse",
|
||||
"key": "warehouse",
|
||||
"type": "text",
|
||||
"description": "Enter warehouse"
|
||||
},
|
||||
"role": {
|
||||
"label": "Role",
|
||||
"key": "role",
|
||||
"type": "text",
|
||||
"description": "Enter role"
|
||||
}
|
||||
},
|
||||
"required": ["account", "username", "password"]
|
||||
}
|
||||
31
plugins/packages/snowflake/lib/operations.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/operations.schema.json",
|
||||
"title": "Snowflake datasource",
|
||||
"description": "A schema defining Snowflake datasource",
|
||||
"type": "database",
|
||||
"defaults": {
|
||||
"mode": "sql"
|
||||
},
|
||||
"properties": {
|
||||
"mode": {
|
||||
"label": "",
|
||||
"key": "mode",
|
||||
"type": "dropdown-component-flip",
|
||||
"description": "Single select dropdown for mode",
|
||||
"list": [
|
||||
{
|
||||
"name": "SQL mode",
|
||||
"value": "sql"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sql": {
|
||||
"query": {
|
||||
"key": "query",
|
||||
"type": "codehinter",
|
||||
"description": "Enter query",
|
||||
"height": "150px"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
plugins/packages/snowflake/lib/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export type SourceOptions = {
|
||||
username: string,
|
||||
account: string,
|
||||
database: string,
|
||||
warehouse: string,
|
||||
schema: string,
|
||||
role: string,
|
||||
password: string,
|
||||
};
|
||||
export type QueryOptions = {
|
||||
query: string;
|
||||
};
|
||||
3272
plugins/packages/snowflake/package-lock.json
generated
Normal file
25
plugins/packages/snowflake/package.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "@tooljet-plugins/snowflake",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo \"Error: run tests from root\" && exit 1",
|
||||
"build": "tsc -b",
|
||||
"clean": "rimraf ./dist && rimraf tsconfig.tsbuildinfo"
|
||||
},
|
||||
"homepage": "https://github.com/tooljet/tooljet#readme",
|
||||
"dependencies": {
|
||||
"@tooljet-plugins/common": "file:../common",
|
||||
"@types/snowflake-sdk": "^1.6.2",
|
||||
"react": "^17.0.2",
|
||||
"snowflake-sdk": "github:ToolJet/snowflake-connector-nodejs#chore/update-https-agent"
|
||||
}
|
||||
}
|
||||
11
plugins/packages/snowflake/tsconfig.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "lib"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
1.0.0
|
||||
1.1.0
|
||||
170
server/package-lock.json
generated
|
|
@ -19,8 +19,8 @@
|
|||
"@nestjs/serve-static": "^2.2.2",
|
||||
"@nestjs/typeorm": "^8.0.0",
|
||||
"@nestjs/websockets": "^8.0.10",
|
||||
"@sentry/node": "^6.12.0",
|
||||
"@sentry/tracing": "^6.12.0",
|
||||
"@sentry/node": "^6.17.6",
|
||||
"@sentry/tracing": "^6.17.6",
|
||||
"@tooljet/plugins": "../plugins",
|
||||
"@types/got": "^9.6.12",
|
||||
"@types/humps": "^2.0.1",
|
||||
|
|
@ -99,8 +99,8 @@
|
|||
"@tooljet-plugins/gcs": "file:packages/gcs",
|
||||
"@tooljet-plugins/googlesheets": "file:packages/googlesheets",
|
||||
"@tooljet-plugins/graphql": "file:packages/graphql",
|
||||
"@tooljet-plugins/minioapi": "file:packages/minioapi",
|
||||
"@tooljet-plugins/mongo": "file:packages/mongo",
|
||||
"@tooljet-plugins/minio": "file:packages/minio",
|
||||
"@tooljet-plugins/mongodb": "file:packages/mongodb",
|
||||
"@tooljet-plugins/mssql": "file:packages/mssql",
|
||||
"@tooljet-plugins/mysql": "file:packages/mysql",
|
||||
"@tooljet-plugins/postgresql": "file:packages/postgresql",
|
||||
|
|
@ -110,8 +110,8 @@
|
|||
"@tooljet-plugins/sendgrid": "file:packages/sendgrid",
|
||||
"@tooljet-plugins/slack": "file:packages/slack",
|
||||
"@tooljet-plugins/stripe": "file:packages/stripe",
|
||||
"@tooljet-plugins/twilioapi": "file:packages/twilioapi",
|
||||
"@tooljet-plugins/typesenseapi": "file:packages/typesenseapi"
|
||||
"@tooljet-plugins/twilio": "file:packages/twilio",
|
||||
"@tooljet-plugins/typesense": "file:packages/typesense"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^27.4.5",
|
||||
|
|
@ -2265,13 +2265,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.17.6.tgz",
|
||||
"integrity": "sha512-wSNsQSqsW8vQ2HEvUEXYOJnzTyVDSWbyH4RHrWV1pQM8zqGx/qfz0sKFM5XFnE9ZeaXKL8LXV3v5i73v+z8lew==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/minimal": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -2280,14 +2281,16 @@
|
|||
},
|
||||
"node_modules/@sentry/core/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/hub": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.17.6.tgz",
|
||||
"integrity": "sha512-Ps9nk+DoFia8jhZ1lucdRE0vDx8hqXOsKXJE8a3hK/Ndki0J9jedYqBeLqSgiFG4qRjXpNFcD6TEM6tnQrv5lw==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -2296,14 +2299,16 @@
|
|||
},
|
||||
"node_modules/@sentry/hub/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/minimal": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.17.6.tgz",
|
||||
"integrity": "sha512-PLGf8WlhtdHuY6ofwYR3nyClr/TYHHAW6i0r62OZCOXTqnFPJorZpAz3VCCP2jMJmbgVbo03wN+u/xAA/zwObA==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -2312,17 +2317,19 @@
|
|||
},
|
||||
"node_modules/@sentry/minimal/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/node": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-SeDDoug2kUxeF1D7JGPa3h5EXxKtmA01mITBPYx5xbJ0sMksnv5I5bC1SJ8arRRzq6+W1C4IEeDBQtrVCk6ixA==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.17.6.tgz",
|
||||
"integrity": "sha512-T1s0yPbGvYpoh9pJgLvpy7s+jVwCyf0ieEoN9rSbnPwbi2vm6MfoV5wtGrE0cBHTPgnyOMv+zq4Q3ww6dfr7Pw==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "6.16.1",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/tracing": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/core": "6.17.6",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/tracing": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"cookie": "^0.4.1",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"lru_map": "^0.3.3",
|
||||
|
|
@ -2344,13 +2351,14 @@
|
|||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/tracing": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.17.6.tgz",
|
||||
"integrity": "sha512-+h5ov+zEm5WH9+vmFfdT4EIqBOW7Tggzh0BDz8QRStRc2JbvEiSZDs+HlsycBwWMQi/ucJs93FPtNnWjW+xvBw==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/minimal": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -2362,17 +2370,19 @@
|
|||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.17.6.tgz",
|
||||
"integrity": "sha512-peGM873lDJtHd/jwW9Egr/hhxLuF0bcPIf2kMZlvEvW/G5GCbuaCR4ArQJlh7vQyma+NLn/XdojpJkC0TomKrw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.17.6.tgz",
|
||||
"integrity": "sha512-RI797N8Ax5yuKUftVX6dc0XmXqo5CN7XqJYPFzYC8udutQ4L8ZYadtUcqNsdz1ZQxl+rp0XK9Q6wjoWmsI2RXA==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/types": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -2381,6 +2391,7 @@
|
|||
},
|
||||
"node_modules/@sentry/utils/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sideway/address": {
|
||||
|
|
@ -14117,61 +14128,68 @@
|
|||
"integrity": "sha512-H5Djcc2txGAINgf3TNaq4yFofYSIK3722PM89S/3R8FuI/eqi1UscajlXk7EBkG9s2pxss/q6SHlpturaavXaw=="
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.17.6.tgz",
|
||||
"integrity": "sha512-wSNsQSqsW8vQ2HEvUEXYOJnzTyVDSWbyH4RHrWV1pQM8zqGx/qfz0sKFM5XFnE9ZeaXKL8LXV3v5i73v+z8lew==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/minimal": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sentry/hub": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.17.6.tgz",
|
||||
"integrity": "sha512-Ps9nk+DoFia8jhZ1lucdRE0vDx8hqXOsKXJE8a3hK/Ndki0J9jedYqBeLqSgiFG4qRjXpNFcD6TEM6tnQrv5lw==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sentry/minimal": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.17.6.tgz",
|
||||
"integrity": "sha512-PLGf8WlhtdHuY6ofwYR3nyClr/TYHHAW6i0r62OZCOXTqnFPJorZpAz3VCCP2jMJmbgVbo03wN+u/xAA/zwObA==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sentry/node": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-SeDDoug2kUxeF1D7JGPa3h5EXxKtmA01mITBPYx5xbJ0sMksnv5I5bC1SJ8arRRzq6+W1C4IEeDBQtrVCk6ixA==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.17.6.tgz",
|
||||
"integrity": "sha512-T1s0yPbGvYpoh9pJgLvpy7s+jVwCyf0ieEoN9rSbnPwbi2vm6MfoV5wtGrE0cBHTPgnyOMv+zq4Q3ww6dfr7Pw==",
|
||||
"requires": {
|
||||
"@sentry/core": "6.16.1",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/tracing": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/core": "6.17.6",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/tracing": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"cookie": "^0.4.1",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"lru_map": "^0.3.3",
|
||||
|
|
@ -14189,13 +14207,14 @@
|
|||
}
|
||||
},
|
||||
"@sentry/tracing": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.17.6.tgz",
|
||||
"integrity": "sha512-+h5ov+zEm5WH9+vmFfdT4EIqBOW7Tggzh0BDz8QRStRc2JbvEiSZDs+HlsycBwWMQi/ucJs93FPtNnWjW+xvBw==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"@sentry/hub": "6.17.6",
|
||||
"@sentry/minimal": "6.17.6",
|
||||
"@sentry/types": "6.17.6",
|
||||
"@sentry/utils": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -14206,19 +14225,22 @@
|
|||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ=="
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.17.6.tgz",
|
||||
"integrity": "sha512-peGM873lDJtHd/jwW9Egr/hhxLuF0bcPIf2kMZlvEvW/G5GCbuaCR4ArQJlh7vQyma+NLn/XdojpJkC0TomKrw=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "6.16.1",
|
||||
"integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==",
|
||||
"version": "6.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.17.6.tgz",
|
||||
"integrity": "sha512-RI797N8Ax5yuKUftVX6dc0XmXqo5CN7XqJYPFzYC8udutQ4L8ZYadtUcqNsdz1ZQxl+rp0XK9Q6wjoWmsI2RXA==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/types": "6.17.6",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
|
|
@ -14281,8 +14303,8 @@
|
|||
"@tooljet-plugins/gcs": "file:packages/gcs",
|
||||
"@tooljet-plugins/googlesheets": "file:packages/googlesheets",
|
||||
"@tooljet-plugins/graphql": "file:packages/graphql",
|
||||
"@tooljet-plugins/minioapi": "file:packages/minioapi",
|
||||
"@tooljet-plugins/mongo": "file:packages/mongo",
|
||||
"@tooljet-plugins/minio": "file:packages/minio",
|
||||
"@tooljet-plugins/mongodb": "file:packages/mongodb",
|
||||
"@tooljet-plugins/mssql": "file:packages/mssql",
|
||||
"@tooljet-plugins/mysql": "file:packages/mysql",
|
||||
"@tooljet-plugins/postgresql": "file:packages/postgresql",
|
||||
|
|
@ -14292,8 +14314,8 @@
|
|||
"@tooljet-plugins/sendgrid": "file:packages/sendgrid",
|
||||
"@tooljet-plugins/slack": "file:packages/slack",
|
||||
"@tooljet-plugins/stripe": "file:packages/stripe",
|
||||
"@tooljet-plugins/twilioapi": "file:packages/twilioapi",
|
||||
"@tooljet-plugins/typesenseapi": "file:packages/typesenseapi",
|
||||
"@tooljet-plugins/twilio": "file:packages/twilio",
|
||||
"@tooljet-plugins/typesense": "file:packages/typesense",
|
||||
"jest": "^27.4.5",
|
||||
"lerna": "^4.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@
|
|||
"@nestjs/serve-static": "^2.2.2",
|
||||
"@nestjs/typeorm": "^8.0.0",
|
||||
"@nestjs/websockets": "^8.0.10",
|
||||
"@sentry/node": "^6.12.0",
|
||||
"@sentry/tracing": "^6.12.0",
|
||||
"@sentry/node": "^6.17.6",
|
||||
"@sentry/tracing": "^6.17.6",
|
||||
"@tooljet/plugins": "../plugins",
|
||||
"@types/got": "^9.6.12",
|
||||
"@types/humps": "^2.0.1",
|
||||
|
|
|
|||
|
|
@ -9,10 +9,13 @@ export class MetadataController {
|
|||
@UseGuards(JwtAuthGuard)
|
||||
@Post('finish_installation')
|
||||
async finishInstallation(@Request() req) {
|
||||
const { name, email } = req.body;
|
||||
const { name, email, org } = req.body;
|
||||
const installedVersion = globalThis.TOOLJET_VERSION;
|
||||
|
||||
await this.metadataService.finishInstallation(installedVersion, name, email);
|
||||
const metadata = await this.metadataService.getMetaData();
|
||||
if (process.env.NODE_ENV == 'production') {
|
||||
await this.metadataService.finishInstallation(metadata, installedVersion, name, email, org);
|
||||
}
|
||||
|
||||
await this.metadataService.updateMetaData({
|
||||
onboarded: true,
|
||||
|
|
@ -55,10 +58,16 @@ export class MetadataController {
|
|||
const updateLastCheckedAt = new Date(data['last_checked'] || null);
|
||||
const diffTime = (now.getTime() - updateLastCheckedAt.getTime()) / 1000;
|
||||
|
||||
if (diffTime > 86400) {
|
||||
const result = await this.metadataService.checkForUpdates(installedVersion, ignoredVersion);
|
||||
latestVersion = result.latestVersion;
|
||||
versionIgnored = false;
|
||||
if (diffTime > 86400 && process.env.NODE_ENV == 'production') {
|
||||
if (process.env.CHECK_FOR_UPDATES) {
|
||||
const result = await this.metadataService.checkForUpdates(installedVersion, ignoredVersion);
|
||||
latestVersion = result.latestVersion;
|
||||
versionIgnored = false;
|
||||
}
|
||||
|
||||
if (!process.env.DISABLED_TOOLJET_TELEMETRY) {
|
||||
await this.metadataService.sendTelemetryData(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export class EmailService {
|
|||
|
||||
constructor() {
|
||||
this.FROM_EMAIL = process.env.DEFAULT_FROM_EMAIL || 'hello@tooljet.io';
|
||||
this.TOOLJET_HOST = process.env.TOOLJET_HOST;
|
||||
this.TOOLJET_HOST = this.stripTrailingSlash(process.env.TOOLJET_HOST);
|
||||
this.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +49,10 @@ export class EmailService {
|
|||
}
|
||||
}
|
||||
|
||||
stripTrailingSlash(hostname: string) {
|
||||
return hostname?.endsWith('/') ? hostname.slice(0, -1) : hostname;
|
||||
}
|
||||
|
||||
async sendWelcomeEmail(to: string, name: string, invitationtoken: string) {
|
||||
const subject = 'Welcome to ToolJet';
|
||||
const inviteUrl = `${this.TOOLJET_HOST}/invitations/${invitationtoken}?signup=true`;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { getManager, Repository } from 'typeorm';
|
||||
import { Metadata } from 'src/entities/metadata.entity';
|
||||
import { gt } from 'semver';
|
||||
const got = require('got');
|
||||
import got from 'got';
|
||||
import { User } from 'src/entities/user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class MetadataService {
|
||||
constructor(
|
||||
|
|
@ -30,16 +32,32 @@ export class MetadataService {
|
|||
async updateMetaData(newOptions: any) {
|
||||
const metadata = await this.metadataRepository.findOne({});
|
||||
|
||||
return await this.metadataRepository.update(metadata.id, { data: { ...metadata.data, ...newOptions } });
|
||||
return await this.metadataRepository.update(metadata.id, {
|
||||
data: { ...metadata.data, ...newOptions },
|
||||
});
|
||||
}
|
||||
|
||||
async finishInstallation(installedVersion: string, name: string, email: string) {
|
||||
return await got('https://hub.tooljet.io/updates', {
|
||||
async finishInstallation(metadata: any, installedVersion: string, name: string, email: string, org: string) {
|
||||
return await got('https://hub.tooljet.io/subscribe', {
|
||||
method: 'post',
|
||||
json: {
|
||||
id: metadata.id,
|
||||
installed_version: installedVersion,
|
||||
name,
|
||||
email,
|
||||
org,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async sendTelemetryData(metadata: Metadata) {
|
||||
const totalUserCount = await getManager().count(User);
|
||||
|
||||
return await got('https://hub.tooljet.io/telemetry', {
|
||||
method: 'post',
|
||||
json: {
|
||||
id: metadata.id,
|
||||
total_users: totalUserCount,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||