Merge branch 'release/v0.5.8' into main

This commit is contained in:
navaneeth 2021-06-16 00:34:02 +05:30
commit d74b3022e9
27 changed files with 365 additions and 98 deletions

View file

@ -39,6 +39,7 @@ gem "mongo", "~> 2"
gem 'aws-sdk', '~> 3'
gem 'kaminari'
gem 'lockbox'
gem 'tiny_tds'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console

View file

@ -1354,6 +1354,7 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
thor (1.1.0)
tiny_tds (2.1.5)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.4)
@ -1394,6 +1395,7 @@ DEPENDENCIES
rubocop-rails
simple_command
spring
tiny_tds
typhoeus
tzinfo-data

View file

@ -0,0 +1,59 @@
class MssqlQueryService
include DatasourceUtils
attr_accessor :data_query, :data_source, :options, :source_options, :current_user
def initialize(data_query, data_source, options, source_options, current_user)
@data_query = data_query
@data_source = data_source
@options = options
@source_options = source_options
@current_user = current_user
end
def self.connection(options)
TinyTds::Client.new(
database: options.dig('database', 'value'),
username: options.dig('username', 'value'),
password: options.dig('password', 'value'),
host: options.dig('host', 'value'),
port: options.dig('port', 'value'),
azure: ActiveModel::Type::Boolean.new.cast(
options.dig('azure', 'value')
) || false
)
end
def process
connection = get_cached_connection(data_source)
connection ||= create_connection
query_text = options['query']
results = connection.execute(query_text)
{ status: 'success', data: results.to_a }
rescue StandardError => e
if connection&.active?
connection&.close
reset_connection(data_source)
end
error = { message: e.message, code: 400 }
end
private
def create_connection
connection = TinyTds::Client.new(
database: source_options['database'],
username: source_options['username'],
password: source_options['password'],
host: source_options['host'],
port: source_options['port'],
azure: ActiveModel::Type::Boolean.new.cast(
source_options['azure']
) || false
)
cache_connection(data_source, connection)
connection
end
end

View file

@ -3,6 +3,7 @@ default: &default
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: tooljet_development
@ -13,6 +14,9 @@ development:
test:
<<: *default
database: tooljet_test
username: postgres
password: <%= ENV.fetch("DB_PASSWORD", '') %>
host: <%= ENV.fetch("DB_HOST", 'localhost') %>
production:
<<: *default
@ -20,4 +24,4 @@ production:
database: <%= ENV['PG_DB'] %>
username: <%= ENV['PG_USER'] %>
password: <%= ENV['PG_PASS'] %>

View file

@ -1,4 +1,4 @@
org = Organization.create(name: 'My organization')
user = User.create(first_name: 'The', last_name: 'Developer', email: 'dev@tooljet.io', password: 'password', organization: org)
OrganizationUser.create(user: user, organization: org, role: 'admin')
OrganizationUser.create(user: user, organization: org, role: 'admin', status: 'active')

View file

@ -4,7 +4,7 @@ services:
base: &base
build:
context: .
dockerfile: ./docker/server.Dockerfile
dockerfile: ./docker/server.Dockerfile.dev
args:
RAILS_ENV: 'development'
tty: true
@ -32,7 +32,7 @@ services:
<<: *base
build:
context: .
dockerfile: ./docker/server.Dockerfile
dockerfile: ./docker/server.Dockerfile.dev
image: tooljet-server:development
volumes:
- ./:/app:delegated
@ -46,14 +46,12 @@ services:
- RAILS_ENV=development
- DB_PASSWORD=postgres
- DB_HOST=postgres
entrypoint: ["bash", "/app/docker/entrypoints/server.sh"]
entrypoint: ["bash", "/app/docker/entrypoints/server.sh"]
command: ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
postgres:
image: postgres:12
restart: always
ports:
- '5432:5432'
volumes:
- postgres:/data/postgres
environment:
@ -61,6 +59,7 @@ services:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
postgres:
node_modules:

View file

@ -1,7 +1,11 @@
#!/bin/bash
set -e
bundle check || bundle install
rake db:create
rake db:migrate
exec "$@"
if [ -e tmp/pids/server.pid ]; then
rm tmp/pids/server.pid
fi
exec "$@"

View file

@ -2,7 +2,8 @@ FROM ruby:2.7.3-buster
RUN apt update && apt install -y \
build-essential \
postgresql
postgresql \
freetds-dev
RUN mkdir -p /app
WORKDIR /app

View file

@ -0,0 +1,18 @@
FROM ruby:2.7.3-buster
RUN apt update && apt install -y \
build-essential \
postgresql \
freetds-dev
RUN mkdir -p /app
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN gem install bundler && bundle install --jobs 20 --retry 5
ENV RAILS_ENV=development
COPY . ./
RUN ["chmod", "755", "docker/entrypoints/server.sh"]

View file

@ -22,11 +22,15 @@ Follow these steps to setup and run ToolJet on Mac OS. Open terminal and run the
$ curl -L https://get.rvm.io | bash -s stable
```
### Install Ruby using RVM
### Install Ruby using RVM
```bash
$ rvm install ruby-2.7.3
$ rvm use 2.7.3
```
### Install [Bundler](https://bundler.io/)
```bash
gem install bundler:2.1.4
```
### Install Node.js
```bash
@ -42,41 +46,52 @@ Follow these steps to setup and run ToolJet on Mac OS. Open terminal and run the
### Install MySQL ( optional )
Skip this step if you do not want to connect to MySQL datasources.
```bash
$ brew install mysql
$ brew install mysql
```
2. ## Setup environment variables
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given here: env variable reference
```bash
$ cp .env.example .env
```
```
3. ## Install Ruby on Rails dependencies
3. ## Populate the keys in the env file.
Run `openssl rand -hex 64` to create secure secrets and use them as the values for `LOCKBOX_MASTER_KEY` and `SECRET_KEY_BASE`.
Example:
```bash
$ cat .env
TOOLJET_HOST=http://localhost:8082
LOCKBOX_MASTER_KEY=c92bcc7f112ffbdd131d1fb6c5005e372b8802f85f6c4586e5a88f57a541382841c8c99e5701b84862e448dd5db846f705321a41bd48a0fed1b58b9596a3877f
SECRET_KEY_BASE=4229d5774cfe7f60e75d6b3bf3a1dbb054a696b6d21b6d5de7b73291899797a222265e12c0a8e8d844f83ebacdf9a67ec42584edf1c2b23e1e7813f8a3339041
```
4. ## Install Ruby on Rails dependencies
```bash
$ bundle
```
4. ## install React dependencies
```bash
5. ## install React dependencies
```bash
$ npm install
```
5. ## Setup Rails server
```bash
6. ## Setup Rails server
```bash
$ bundle exec rake db:create
$ bundle exec rake db:reset
$ bundle exec rails server
```
6. ## Create login credentials
7. ## Create login credentials
1. Open rails console using:
1. Open rails console using:
```bash
```bash
$ bundle exec rails console
```
2. Create a new organization
2. Create a new organization
```ruby
Organization.create(name: 'Dev')
```
@ -88,12 +103,12 @@ Follow these steps to setup and run ToolJet on Mac OS. Open terminal and run the
4. Add user to the organization as admin
```ruby
OrganizationUser.create(user: User.first, organization: Organization.first, role: 'admin')
OrganizationUser.create(user: User.first, organization: Organization.first, role: 'admin', status: 'active')
```
7. ## Running the React frontend ( Client )
```bash
8. ## Running the React frontend ( Client )
```bash
$ cd ./frontend && npm start
```
The client will start running on the port 8082, you can access the client by visiting: [https://localhost:8082](https://localhost:8082 )
The client will start running on the port 8082, you can access the client by visiting: [https://localhost:8082](https://localhost:8082)

View file

@ -7,12 +7,12 @@ Docker compose is the easiest way to setup ToolJet server and client locally.
## Prerequisites
Make sure you have the latest version of `docker` and `docker-compose` installed.
Make sure you have the latest version of `docker` and `docker-compose` installed.
[Official docker installation guide](https://docs.docker.com/desktop/)
[Official docker installation guide](https://docs.docker.com/desktop/)
[Official docker-compose installation guide](https://docs.docker.com/compose/install/)
We recommend:
We recommend:
```bash
$ docker --version
Docker version 19.03.12, build 48a66213fe
@ -20,11 +20,11 @@ $ docker-compose --version
docker-compose version 1.26.2, build eefe0d31
```
## Setting up
## Setting up
1. Close the repository
```bash
$ git clone https://github.com/tooljet/tooljet.git
$ git clone https://github.com/tooljet/tooljet.git
```
2. Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given here: env variable reference
@ -32,9 +32,19 @@ $ git clone https://github.com/tooljet/tooljet.git
$ cp .env.example .env
```
3. Build docker images
3. Populate the keys in the `.env` file. Run `openssl rand -hex 64` to create secure secrets and use them as the values for `LOCKBOX_MASTER_KEY` and `SECRET_KEY_BASE`.
Example:
```bash
$ docker-compose build
$ cat .env
TOOLJET_HOST=http://localhost:8082
LOCKBOX_MASTER_KEY=c92bcc7f112ffbdd131d1fb6c5005e372b8802f85f6c4586e5a88f57a541382841c8c99e5701b84862e448dd5db846f705321a41bd48a0fed1b58b9596a3877f
SECRET_KEY_BASE=4229d5774cfe7f60e75d6b3bf3a1dbb054a696b6d21b6d5de7b73291899797a222265e12c0a8e8d844f83ebacdf9a67ec42584edf1c2b23e1e7813f8a3339041
```
4. Build docker images
```bash
$ docker-compose build
```
4. ToolJet server is built using Ruby on Rails. You have to reset the database if building for the first time.
@ -47,51 +57,29 @@ $ docker-compose run server rails db:reset
$ docker-compose up
```
6. Creating login credentials
1. Open rails console using:
```bash
$ docker-compose run server rails console
```
2. Create a new organization
```ruby
Organization.create(name: 'Dev')
```
3. Create a new user
```ruby
User.create(first_name: 'dev', email: 'dev@tooljet.io', password: 'password', organization: Organization.first)
```
4. Add user to the organization as admin
```ruby
OrganizationUser.create(user: User.first, organization: Organization.first, role: 'admin')
```
6. The app should now be served locally at http://localhost:8082/. You can login using the default user created.
[ email: dev@tooljet.io
password: password
]
7. To shut down the containers,
```bash
$ docker-compose down
$ docker-compose stop
```
## Running Rails tests
## Running Rails tests
To run all the tests
To run all the tests
```bash
$ docker-compose run server rails test
```bash
$ docker-compose run server rails test
```
To run a specific test
```bash
To run a specific test
```bash
$ docker-compose run server rails test <path-to-file>:<line:number>
```
## Troubleshooting
If there is any change to dockerfiles, Gemfile or package.json, rebuild the images using:
```bash
$ docker-compose up --build
```
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.

View file

@ -16,7 +16,7 @@ Follow these steps to setup and run ToolJet on Ubuntu. Open terminal and run the
$ sudo apt-get install rvm
```
### Install Ruby using RVM
### Install Ruby using RVM
```bash
$ rvm install ruby-2.7.3
$ rvm use 2.7.3
@ -38,34 +38,46 @@ Follow these steps to setup and run ToolJet on Ubuntu. Open terminal and run the
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given here: env variable reference
```bash
$ cp .env.example .env
```
```
3. ## Install Ruby on Rails dependencies
3. ## Populate the keys in the env file.
Run `openssl rand -hex 64` to create secure secrets and use them as the values for `LOCKBOX_MASTER_KEY` and `SECRET_KEY_BASE`.
Example:
```bash
$ cat .env
TOOLJET_HOST=http://localhost:8082
LOCKBOX_MASTER_KEY=c92bcc7f112ffbdd131d1fb6c5005e372b8802f85f6c4586e5a88f57a541382841c8c99e5701b84862e448dd5db846f705321a41bd48a0fed1b58b9596a3877f
SECRET_KEY_BASE=4229d5774cfe7f60e75d6b3bf3a1dbb054a696b6d21b6d5de7b73291899797a222265e12c0a8e8d844f83ebacdf9a67ec42584edf1c2b23e1e7813f8a3339041
```
4. ## Install Ruby on Rails dependencies
```bash
$ bundle
```
4. ## install React dependencies
```bash
5. ## install React dependencies
```bash
$ npm install
```
5. ## Setup Rails server
```bash
6. ## Setup Rails server
```bash
$ bundle exec rake db:create
$ bundle exec rake db:reset
$ bundle exec rails server
```
6. ## Create login credentials
7. ## Create login credentials
1. Open rails console using:
1. Open rails console using:
```bash
```bash
$ bundle exec rails console
```
2. Create a new organization
2. Create a new organization
```ruby
Organization.create(name: 'Dev')
```
@ -77,12 +89,12 @@ Follow these steps to setup and run ToolJet on Ubuntu. Open terminal and run the
4. Add user to the organization as admin
```ruby
OrganizationUser.create(user: User.first, organization: Organization.first, role: 'admin')
OrganizationUser.create(user: User.first, organization: Organization.first, role: 'admin', status: 'active')
```
7. ## Running the React frontend ( Client )
```bash
8. ## Running the React frontend ( Client )
```bash
$ cd ./frontend && npm start
```
The client will start running on the port 8082, you can access the client by visiting: [https://localhost:8082](https://localhost:8082 )
The client will start running on the port 8082, you can access the client by visiting: [https://localhost:8082](https://localhost:8082)

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -278,7 +278,7 @@ export function Table({
onBlur={(e) => {
handleCellValueChange(cell.row.index, column.key || column.name, e.target.value, cell.row.original);
}}
value={cellValue}
defaultValue={cellValue}
>
</textarea>;
} if (columnType === 'dropdown') {

View file

@ -148,7 +148,7 @@ class DataSourceManager extends React.Component {
{selectedDataSource && (
<div className="row">
<img
src={`/assets/images/icons/editor/datasources/${dataSourceMeta.name.toLowerCase()}.svg`}
src={`/assets/images/icons/editor/datasources/${dataSourceMeta.kind.toLowerCase()}.svg`}
style={{ objectFit: 'contain' }}
height="25"
width="25"
@ -260,7 +260,7 @@ class DataSourceManager extends React.Component {
<div className="col-auto">
<Button className={`m-2 ${isSaving ? 'btn-loading' : ''}`} disabled={isSaving} variant="primary" onClick={this.createDataSource}>
{'Save'}
</Button>
</Button>
</div>
</Modal.Footer>

View file

@ -31,6 +31,22 @@ export const dataBaseSources = [
password: { type: 'string', encrypted: true }
}
},
{
name: 'SQL Server',
kind: 'mssql',
exposedVariables: {
isLoading: {},
data: {},
rawData: {}
},
options: {
host: { type: 'string' },
port: { type: 'string' },
database: { type: 'string' },
username: { type: 'string' },
password: { type: 'string', encrypted: true }
}
},
{
name: 'MongoDB',
kind: 'mongodb',

View file

@ -13,6 +13,13 @@ export const defaultOptions = {
username: { value: '' },
password: { value: '' }
},
mssql: {
host: { value: 'localhost' },
port: { value: 1433 },
database: { value: '' },
username: { value: '' },
password: { value: '' }
},
redis: {
host: { value: 'localhost' },
port: { value: 6379 },
@ -28,7 +35,7 @@ export const defaultOptions = {
connection_type: { value: 'manual' },
connection_string: { value: ''}
},
elasticsearch: {
scheme: { value: 'https' },
host: { value: 'localhost' },

View file

@ -0,0 +1,80 @@
import React from 'react';
export const Mssql = ({
optionchanged, options
}) => {
return (
<div>
<div className="row">
<div className="col-md-9">
<label className="form-label">Host</label>
<input
type="text"
className="form-control"
onChange={(e) => optionchanged('host', e.target.value)}
value={options.host.value}
/>
</div>
<div className="col-md-3">
<label className="form-label">Port</label>
<input
type="text"
className="form-control"
onChange={(e) => optionchanged('port', e.target.value)}
value={options.port.value}
/>
</div>
</div>
<div className="row mt-3">
<div className="col-md-4">
<label className="form-label">Database Name</label>
<input
type="text"
className="form-control"
onChange={(e) => optionchanged('database', e.target.value)}
value={options.database.value}
/>
</div>
<div className="col-md-4">
<label className="form-label">Username</label>
<input
type="text"
className="form-control"
onChange={(e) => optionchanged('username', e.target.value)}
value={options.username.value}
/>
</div>
<div className="col-md-4">
<label className="form-label">
Password
<small className="text-green mx-2">
<img className="mx-2 encrypted-icon encrypted-icon" src="/assets/images/icons/padlock.svg" width="12" height="12" />
<span className="pt-2">Encrypted</span>
</small>
</label>
<input
type="password"
className="form-control"
onChange={(e) => optionchanged('password', e.target.value)}
value={options.password.value}
/>
</div>
</div>
<div className="row mt-3">
<div className="col-md-3">
<div className="field mb-3">
<label className="form-check my-2">
<input
className="form-check-input"
type="checkbox"
defaultChecked={false}
onClick={(e) => { optionchanged('azure', e.target.checked) } }
/>
Azure
</label>
</div>
</div>
</div>
</div>
);
};

View file

@ -10,6 +10,7 @@ import { Slack } from './Slack';
import { Mongodb } from './Mongodb';
import { Dynamodb } from './Dynamodb';
import { Airtable } from './Airtable';
import { Mssql } from './Mssql';
export const SourceComponents = {
Elasticsearch,
@ -23,5 +24,6 @@ export const SourceComponents = {
Slack,
Mongodb,
Dynamodb,
Airtable
Airtable,
Mssql
};

View file

@ -230,6 +230,16 @@ class Editor extends React.Component {
this.computeComponentState(newDefinition.components);
};
handleInspectorView = (component) => {
if (this.state.selectedComponent.hasOwnProperty('component')) {
const { id: selectedComponentId } = this.state.selectedComponent;
if (selectedComponentId === component.id) {
this.setState({selectedComponent: null})
this.switchSidebarTab(2);
}
}
}
removeComponent = (component) => {
let newDefinition = this.state.appDefinition;
@ -241,7 +251,7 @@ class Editor extends React.Component {
delete newDefinition.components[component.id];
this.appDefinitionChanged(newDefinition);
this.switchSidebarTab(2);
this.handleInspectorView(component);
};
componentDefinitionChanged = (newDefinition) => {

View file

@ -93,13 +93,11 @@ export const EventSelector = ({
{definition.actionId === 'show-alert' && (
<div className="p-1">
<label className="form-label mt-1">Message</label>
<input
onBlur={(e) => eventOptionUpdated(param, 'message', e.target.value, extraData)}
value={message}
type="text"
className="form-control form-control-sm"
placeholder="Text goes here"
<CodeHinter
currentState={currentState}
onChange={(value) => eventOptionUpdated(param, 'message', value, extraData)}
/>
</div>
)}

View file

@ -7,7 +7,7 @@ import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import Popover from 'react-bootstrap/Popover';
import ReactTooltip from 'react-tooltip';
import { toast } from 'react-toastify';
import { validateQueryName } from '@/_helpers/utils';
import { validateQueryName, convertToKebabCase } from '@/_helpers/utils';
export const Inspector = ({
selectedComponentId,
@ -244,7 +244,7 @@ export const Inspector = ({
</div>
<div className="widget-documentation-link p-2">
<a href={`https://docs.tooljet.io/docs/widgets/${componentMeta.name.toLowerCase()}`} target="_blank">
<a href={`https://docs.tooljet.io/docs/widgets/${convertToKebabCase(componentMeta?.name ?? '')}`} target="_blank">
<small>
{componentMeta.name} documentation
</small>

View file

@ -0,0 +1,41 @@
import React from 'react';
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
import { changeOption } from './utils';
class Mssql extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.setState({
options: this.props.options
});
}
render() {
const { options } = this.state;
return (
<div>
{options && (
<div className="mb-3 mt-2">
<CodeHinter
currentState={this.props.currentState}
initialValue={options.query}
mode="sql"
theme="duotone-light"
lineNumbers={true}
className="query-hinter"
onChange={(value) => changeOption(this, 'query', value)}
/>
</div>
)}
</div>
);
}
}
export { Mssql };

View file

@ -10,6 +10,7 @@ import { Slack } from './Slack';
import { Mongodb } from './Mongodb';
import { Dynamodb } from './Dynamodb';
import { Airtable } from './Airtable';
import { Mssql } from './Mssql';
export const allSources = {
Restapi,
@ -23,5 +24,6 @@ export const allSources = {
Slack,
Mongodb,
Dynamodb,
Airtable
Airtable,
Mssql
};

View file

@ -32,5 +32,8 @@ export const defaultOptions = {
},
airtable: {
},
mssql: {
}
};

View file

@ -88,7 +88,8 @@ async function copyToClipboard(text) {
function executeAction(_ref, event) {
if (event) {
if (event.actionId === 'show-alert') {
toast(event.options.message, { hideProgressBar: true });
const message = resolveReferences(event.options.message, _ref.state.currentState);
toast(message, { hideProgressBar: true });
}
if (event.actionId === 'open-webpage') {

View file

@ -129,3 +129,6 @@ export function validateQueryName(name){
const nameRegex = new RegExp('^[A-Za-z0-9_-]*$');
return nameRegex.test(name);
};
export const convertToKebabCase = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase()