diff --git a/README.md b/README.md index 8d61489002..c1c3f025db 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ ToolJet is an **open-source no-code framework** to build and deploy internal too

- +

@@ -21,7 +21,8 @@ ToolJet is an **open-source no-code framework** to build and deploy internal too ## Features - Visual app builder with widgets such as tables, charts, modals, buttons, dropdowns and more -- Mobile & desktop layouts +- Mobile 📱 & desktop layouts 🖥 +- Dark mode 🌛 - Connect to databases, APIs and external services - Deploy on-premise ( supports docker, kubernetes, heroku and more ) - Granular access control on organization level and app level diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index d672697283..9aec230539 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Channel < ActionCable::Channel::Base end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index 0ff5442f47..8d6c2a1bf4 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Connection < ActionCable::Connection::Base end diff --git a/app/commands/authenticate_user.rb b/app/commands/authenticate_user.rb index c66bade77a..dc8189988e 100644 --- a/app/commands/authenticate_user.rb +++ b/app/commands/authenticate_user.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AuthenticateUser prepend SimpleCommand @@ -20,7 +22,7 @@ class AuthenticateUser return user if user && user.authenticate(password) && org_user.active? - errors.add :user_authentication, 'invalid credentials' + errors.add :user_authentication, "invalid credentials" nil end end diff --git a/app/commands/authorize_api_request.rb b/app/commands/authorize_api_request.rb index 2fab5f98a1..eb6a0a2f35 100644 --- a/app/commands/authorize_api_request.rb +++ b/app/commands/authorize_api_request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AuthorizeApiRequest prepend SimpleCommand @@ -15,12 +17,12 @@ class AuthorizeApiRequest def user @user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token - @user || errors.add(:token, 'Invalid token') && nil + @user || errors.add(:token, "Invalid token") && nil org_user = OrganizationUser.where(user: @user, organization: @user.organization)&.first @user = nil unless org_user.active? - @user || errors.add(:token, 'Archived user') && nil + @user || errors.add(:token, "Archived user") && nil end def decoded_auth_token @@ -28,10 +30,10 @@ class AuthorizeApiRequest end def http_auth_header - if headers['Authorization'].present? - return headers['Authorization'].split(' ').last + if headers["Authorization"].present? + return headers["Authorization"].split(" ").last else - errors.add(:token, 'Missing token') + errors.add(:token, "Missing token") end nil diff --git a/app/controllers/apps_controller.rb b/app/controllers/apps_controller.rb index 53316db7c6..85bcb32c6c 100644 --- a/app/controllers/apps_controller.rb +++ b/app/controllers/apps_controller.rb @@ -15,7 +15,7 @@ class AppsController < ApplicationController @scope = @folder.apps end - @apps = @scope.order('created_at desc') + @apps = @scope.order("created_at desc") .page(params[:page]) .per(10) .includes(:user) @@ -31,12 +31,12 @@ class AppsController < ApplicationController def create authorize App @app = App.create!({ - name: 'Untitled app', + name: "Untitled app", organization: @current_user.organization, - current_version: AppVersion.new(name: 'v0'), + current_version: AppVersion.new(name: "v0"), user: @current_user }) - AppUser.create(app: @app, user: @current_user, role: 'admin') + AppUser.create(app: @app, user: @current_user, role: "admin") end def show diff --git a/app/controllers/data_sources_controller.rb b/app/controllers/data_sources_controller.rb index f3cc43dc82..d5129091b5 100644 --- a/app/controllers/data_sources_controller.rb +++ b/app/controllers/data_sources_controller.rb @@ -2,6 +2,11 @@ class DataSourcesController < ApplicationController def index + app = App.find_by_id params[:app_id] + unless AppPolicy.new(@current_user, app).update? + render json: { message: "Insufficient permissions" }, status: :internal_server_error + return + end @data_sources = DataSource.where(app_id: params[:app_id]) end diff --git a/app/controllers/forgot_password_controller.rb b/app/controllers/forgot_password_controller.rb index ddad4390df..b89d379987 100644 --- a/app/controllers/forgot_password_controller.rb +++ b/app/controllers/forgot_password_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ForgotPasswordController < ApplicationController skip_before_action :authenticate_request @@ -5,9 +7,9 @@ class ForgotPasswordController < ApplicationController user = User.find_by(email: params[:_json]) if user.present? user.send_password_reset - render json: {message: "We've sent the confirmation code to your email address"}, status: :ok + render json: { message: "We've sent the confirmation code to your email address" }, status: :ok else - render json: {error: 'Email address is not associated with a ToolJet cloud account.'}, status: :not_found + render json: { error: "Email address is not associated with a ToolJet cloud account." }, status: :not_found end end @@ -15,12 +17,12 @@ class ForgotPasswordController < ApplicationController user = User.find_by(forgot_password_token: params[:token]) if user.present? && user.forgot_password_token_valid? if user.reset_password(params[:password]) - render json: {message: 'Your password has been successfuly reset!'}, status: :ok + render json: { message: "Your password has been successfuly reset!" }, status: :ok else - render json: {error: user.errors.full_messages}, status: :unprocessable_entity + render json: { error: user.errors.full_messages }, status: :unprocessable_entity end else - render json: {error: 'Link not valid or expired.'}, status: :not_found + render json: { error: "Link not valid or expired." }, status: :not_found end end end diff --git a/app/controllers/metadata_controller.rb b/app/controllers/metadata_controller.rb index c6d3a58e94..0954b9202c 100644 --- a/app/controllers/metadata_controller.rb +++ b/app/controllers/metadata_controller.rb @@ -1,7 +1,8 @@ +# frozen_string_literal: true + class MetadataController < ApplicationController def index - - unless ENV.fetch('CHECK_FOR_UPDATES', true) + unless ENV.fetch("CHECK_FOR_UPDATES", true) return end @@ -20,7 +21,7 @@ class MetadataController < ApplicationController data = metadata.data end - render json: { + render json: { latest_version: data["latest_version"], installed_version: installed_version, version_ignored: data["version_ignored"], @@ -42,11 +43,10 @@ class MetadataController < ApplicationController end def finish_installation - name = params[:name] email = params[:email] - response = HTTParty.post('https://hub.tooljet.io/subscribe', + response = HTTParty.post("https://hub.tooljet.io/subscribe", verify: false, body: { name: name, email: email, installed_version: TOOLJET_VERSION }.to_json, headers: { "Content-Type" => "application/json" }) @@ -56,23 +56,22 @@ class MetadataController < ApplicationController Metadatum.first.update(data: data) end - private - def check_for_updates(current_data, installed_version) + private + def check_for_updates(current_data, installed_version) + response = HTTParty.post("https://hub.tooljet.io/updates", + verify: false, + body: { installed_version: installed_version }.to_json, + headers: { "Content-Type" => "application/json" }) - response = HTTParty.post('https://hub.tooljet.io/updates', - verify: false, - body: { installed_version: installed_version }.to_json, - headers: { "Content-Type" => "application/json" }) + data = JSON.parse(response.body) + latest_version = data["latest_version"] - data = JSON.parse(response.body) - latest_version = data["latest_version"] - - if latest_version > '0.5.3' && latest_version != current_data["ignored_version"] - current_data["latest_version"] = latest_version - current_data["version_ignored"] = false - end - - current_data["last_checked"] = Time.now - Metadatum.first.update(data: current_data) + if latest_version > "0.5.3" && latest_version != current_data["ignored_version"] + current_data["latest_version"] = latest_version + current_data["version_ignored"] = false end + + current_data["last_checked"] = Time.now + Metadatum.first.update(data: current_data) + end end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index d394c3d106..bef395997d 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationJob < ActiveJob::Base # Automatically retry jobs that encountered a deadlock # retry_on ActiveRecord::Deadlocked diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 050b7d056e..adf620ba03 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,6 @@ +# frozen_string_literal: true + class ApplicationMailer < ActionMailer::Base default from: "ToolJet <#{ENV.fetch('DEFAULT_FROM_EMAIL', 'hello@tooljet.io')}>" - layout 'mailer' + layout "mailer" end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 69713ddbd0..4c9fbecb2a 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -1,15 +1,17 @@ +# frozen_string_literal: true + class UserMailer < ApplicationMailer def invitation_email @user = params[:user] @sender = params[:sender] @url = "#{ENV.fetch('TOOLJET_HOST')}/invitations/#{@user.invitation_token}" - mail(to: @user.email, subject: 'ToolJet Invitation') + mail(to: @user.email, subject: "ToolJet Invitation") end def new_signup_email @user = params[:user] @url = "#{ENV.fetch('TOOLJET_HOST')}/invitations/#{@user.invitation_token}?signup=true" - mail(to: @user.email, subject: 'ToolJet Invitation') + mail(to: @user.email, subject: "ToolJet Invitation") end def password_reset(user) diff --git a/app/models/metadatum.rb b/app/models/metadatum.rb index e0e1227213..56d69a7e18 100644 --- a/app/models/metadatum.rb +++ b/app/models/metadatum.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class Metadatum < ApplicationRecord end diff --git a/app/models/user.rb b/app/models/user.rb index 13695cf6ba..8efcb7bb39 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -53,7 +53,7 @@ class User < ApplicationRecord private - def generate_base64_token - SecureRandom.urlsafe_base64 - end + def generate_base64_token + SecureRandom.urlsafe_base64 + end end diff --git a/app/policies/app_policy.rb b/app/policies/app_policy.rb index 306c59e719..8725177f0e 100644 --- a/app/policies/app_policy.rb +++ b/app/policies/app_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AppPolicy < ApplicationPolicy attr_reader :user, :app diff --git a/app/policies/app_user_policy.rb b/app/policies/app_user_policy.rb index ac3fd1b4f9..e5f731c763 100644 --- a/app/policies/app_user_policy.rb +++ b/app/policies/app_user_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AppUserPolicy < ApplicationPolicy attr_reader :user, :app_user diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb index eefe976c6c..50f2d7c616 100644 --- a/app/policies/application_policy.rb +++ b/app/policies/application_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationPolicy attr_reader :user, :record diff --git a/app/policies/organization_user_policy.rb b/app/policies/organization_user_policy.rb index 08f9a0cde8..127ca39ec2 100644 --- a/app/policies/organization_user_policy.rb +++ b/app/policies/organization_user_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class OrganizationUserPolicy < ApplicationPolicy attr_reader :user, :organization_user diff --git a/app/services/airtable_query_service.rb b/app/services/airtable_query_service.rb index 442b216880..4326eef575 100644 --- a/app/services/airtable_query_service.rb +++ b/app/services/airtable_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AirtableQueryService attr_accessor :query, :source, :options, :source_options, :current_user @@ -10,16 +12,16 @@ class AirtableQueryService end def process - operation = options['operation'] - api_key = source_options['api_key'] + operation = options["operation"] + api_key = source_options["api_key"] error = false - if operation === 'list_records' - - base_id = options['base_id'] - table_name = options['table_name'] - page_size = options['page_size'] - offset = options['offset'] + if operation === "list_records" + + base_id = options["base_id"] + table_name = options["table_name"] + page_size = options["page_size"] + offset = options["offset"] result = list_records(api_key, base_id, table_name, page_size, offset) @@ -27,11 +29,11 @@ class AirtableQueryService error = result.code != 200 end - if operation === 'retrieve_record' - - base_id = options['base_id'] - table_name = options['table_name'] - record_id = options['record_id'] + if operation === "retrieve_record" + + base_id = options["base_id"] + table_name = options["table_name"] + record_id = options["record_id"] result = retrieve_record(api_key, base_id, table_name, record_id) @@ -40,28 +42,26 @@ class AirtableQueryService end if error - { status: 'error', code: 500, message: data["message"], data: data } + { status: "error", code: 500, message: data["message"], data: data } else - { status: 'success', data: data } + { status: "success", data: data } end end private def list_records(api_key, base_id, table_name, page_size, offset) - result = HTTParty.get(URI.encode("https://api.airtable.com/v0/#{base_id}/#{table_name}"), - headers: { 'Content-Type': - 'application/json', "Authorization": "Bearer #{api_key}" }) + headers: { "Content-Type": + "application/json", "Authorization": "Bearer #{api_key}" }) result end def retrieve_record(api_key, base_id, table_name, record_id) - result = HTTParty.get(URI.encode("https://api.airtable.com/v0/#{base_id}/#{table_name}/#{record_id}"), - headers: { 'Content-Type': - 'application/json', "Authorization": "Bearer #{api_key}" }) + headers: { "Content-Type": + "application/json", "Authorization": "Bearer #{api_key}" }) result end diff --git a/app/services/concerns/datasource_utils.rb b/app/services/concerns/datasource_utils.rb index 668a14fcaa..90499a1230 100644 --- a/app/services/concerns/datasource_utils.rb +++ b/app/services/concerns/datasource_utils.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + module DatasourceUtils extend ActiveSupport::Concern def get_cached_connection(data_source) - connection = nil if $connections.include? data_source.id data = $connections[data_source.id] @@ -10,8 +11,7 @@ module DatasourceUtils connection = $connections[data_source.id][:connection] end end - - connection + connection end def cache_connection(data_source, connection) @@ -21,6 +21,4 @@ module DatasourceUtils def reset_connection(data_source) $connections.delete @data_source.id end - end - \ No newline at end of file diff --git a/app/services/credential_service.rb b/app/services/credential_service.rb index 03068b406f..a5ddf08254 100644 --- a/app/services/credential_service.rb +++ b/app/services/credential_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CredentialService def initialize; end @@ -6,10 +8,10 @@ class CredentialService options.keys.each do |key| option = options[key] - parsed_options[key] = if option['encrypted'] - Credential.find(option['credential_id']).value + parsed_options[key] = if option["encrypted"] + Credential.find(option["credential_id"]).value else - option['value'] + option["value"] end end diff --git a/app/services/data_source_connection_service.rb b/app/services/data_source_connection_service.rb index 64bc6b83c0..1d73d9b82d 100644 --- a/app/services/data_source_connection_service.rb +++ b/app/services/data_source_connection_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DataSourceConnectionService attr_accessor :data_source_kind, :options diff --git a/app/services/dynamodb_query_service.rb b/app/services/dynamodb_query_service.rb index 24fcad567b..fd6ccd7630 100644 --- a/app/services/dynamodb_query_service.rb +++ b/app/services/dynamodb_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DynamodbQueryService attr_accessor :data_query, :data_source, :options, :source_options, :current_user @@ -9,16 +11,15 @@ class DynamodbQueryService @current_user = current_user end - def self.connection options - - region = options.dig('region', 'value') - access_key = options.dig('access_key', 'value') - secret_key = options.dig('secret_key', 'value') + def self.connection(options) + region = options.dig("region", "value") + access_key = options.dig("access_key", "value") + secret_key = options.dig("secret_key", "value") credentials = Aws::Credentials.new(access_key, secret_key) dynamodb = Aws::DynamoDB::Client.new(region: region, credentials: credentials) - - dynamodb.list_tables + + dynamodb.list_tables end def process @@ -36,17 +37,17 @@ class DynamodbQueryService error = e.message end - { status: error ? 'failed' : 'success', data: data, error: { message: error } } + { status: error ? "failed" : "success", data: data, error: { message: error } } end - private + private def get_connection if $connections.include? data_source.id connection = $connections[data_source.id][:connection] else - region = source_options['region'] - access_key = source_options['access_key'] - secret_key = source_options['secret_key'] + region = source_options["region"] + access_key = source_options["access_key"] + secret_key = source_options["secret_key"] credentials = Aws::Credentials.new(access_key, secret_key) connection = Aws::DynamoDB::Client.new(region: region, credentials: credentials) @@ -58,7 +59,7 @@ class DynamodbQueryService end def exec_list_tables(connection, options) - tables = connection.list_tables + tables = connection.list_tables tables.to_h end @@ -70,7 +71,7 @@ class DynamodbQueryService table_name: table, key: key } - + connection.get_item(item).to_h end diff --git a/app/services/elasticsearch_query_service.rb b/app/services/elasticsearch_query_service.rb index 40c39e0814..e8f8b4c2dc 100644 --- a/app/services/elasticsearch_query_service.rb +++ b/app/services/elasticsearch_query_service.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + class ElasticsearchQueryService include DatasourceUtils - require 'elasticsearch' + require "elasticsearch" attr_accessor :data_query, :data_source, :options, :source_options, :current_user @@ -12,27 +14,26 @@ class ElasticsearchQueryService @data_source = data_source end - def self.connection options - - scheme = options.dig('scheme', 'value') - host = options.dig('host', 'value') - port = options.dig('port', 'value') - username = options.dig('username', 'value') - password = options.dig('password', 'value') + def self.connection(options) + scheme = options.dig("scheme", "value") + host = options.dig("host", "value") + port = options.dig("port", "value") + username = options.dig("username", "value") + password = options.dig("password", "value") unless username.blank? || password.blank? url = "#{scheme}://#{username}:#{password}@#{host}:#{port}" else url = "#{scheme}://#{host}:#{port}" end - + client = Elasticsearch::Client.new( url: url, retry_on_failure: 5, request_timeout: 15, adapter: :typhoeus ) - + client.info # Try to fetch cluster info end @@ -44,32 +45,32 @@ class ElasticsearchQueryService connection = create_connection unless connection begin - operation = options['operation'] + operation = options["operation"] - if operation == 'search' - index = options['index'] - query = JSON.parse(options['query']) + if operation == "search" + index = options["index"] + query = JSON.parse(options["query"]) data = connection.search(index: index, body: query) end - if operation == 'index_document' - index = options['index'] - body = options['body'] + if operation == "index_document" + index = options["index"] + body = options["body"] data = connection.index(index: index, body: body) end - if operation == 'get' - index = options['index'] - id = options['id'] + if operation == "get" + index = options["index"] + id = options["id"] data = connection.get(index: index, id: id) end - if operation == 'update' - index = options['index'] - id = options['id'] - body = options['body'] + if operation == "update" + index = options["index"] + id = options["id"] + body = options["body"] data = connection.update(index: index, id: id, body: body) end @@ -82,14 +83,13 @@ class ElasticsearchQueryService { data: data, error: error } end - private + private def create_connection - - scheme = source_options['scheme'] - host = source_options['host'] - port = source_options['port'] - username = source_options['username'] - password = source_options['password'] + scheme = source_options["scheme"] + host = source_options["host"] + port = source_options["port"] + username = source_options["username"] + password = source_options["password"] unless username.blank? || password.blank? url = "#{scheme}://#{username}:#{password}@#{host}:#{port}" diff --git a/app/services/firestore_query_service.rb b/app/services/firestore_query_service.rb index 96ac04904e..5263500893 100644 --- a/app/services/firestore_query_service.rb +++ b/app/services/firestore_query_service.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + class FirestoreQueryService - require 'google/cloud/firestore' + require "google/cloud/firestore" include DatasourceUtils attr_accessor :data_query, :options, :source_options, :current_user, :data_source @@ -12,8 +14,8 @@ class FirestoreQueryService @current_user = current_user end - def self.connection options - gcp_key = JSON.parse(options.dig('gcp_key', 'value')) + def self.connection(options) + gcp_key = JSON.parse(options.dig("gcp_key", "value")) Google::Cloud::Firestore.configure do |config| config.credentials = gcp_key @@ -33,14 +35,14 @@ class FirestoreQueryService firestore = get_cached_connection(data_source) firestore = create_connection unless firestore - operation = options['operation'] + operation = options["operation"] - update_document(options['path'], options['body'].as_json, firestore) if operation == 'update_document' + update_document(options["path"], options["body"].as_json, firestore) if operation == "update_document" - if operation == 'bulk_update' - records = options['records'] - collection = options['collection'] - doc_key_id = options['document_id_key'] + if operation == "bulk_update" + records = options["records"] + collection = options["collection"] + doc_key_id = options["document_id_key"] records.each do |record| path = "#{collection}/#{record[doc_key_id]}" @@ -49,50 +51,50 @@ class FirestoreQueryService end end - if operation == 'get_document' - path = options['path'] + if operation == "get_document" + path = options["path"] doc_ref = firestore.doc path snapshot = doc_ref.get data = snapshot.data end - if operation == 'set_document' - path = options['path'] - body = JSON.parse(options['body']) + if operation == "set_document" + path = options["path"] + body = JSON.parse(options["body"]) doc_ref = firestore.doc path doc_ref.set body end - if operation == 'add_document' - path = options['path'] - body = JSON.parse(options['body']) + if operation == "add_document" + path = options["path"] + body = JSON.parse(options["body"]) col_ref = firestore.col path col_ref.add body end - if operation == 'delete_document' - path = options['path'] - body = JSON.parse(options['body']) + if operation == "delete_document" + path = options["path"] + body = JSON.parse(options["body"]) doc_ref = firestore.doc path doc_ref.delete end - if operation == 'query_collection' - path = options['path'] + if operation == "query_collection" + path = options["path"] doc_ref = firestore.col path # execute where condition - if options['where_field'] - doc_ref = doc_ref.where options['where_field'], options['where_operation'], options['where_value'] + if options["where_field"] + doc_ref = doc_ref.where options["where_field"], options["where_operation"], options["where_value"] end - if options['order'] - doc_ref = doc_ref.order(options['order'], 'desc') + if options["order"] + doc_ref = doc_ref.order(options["order"], "desc") end - if options['limit'] - doc_ref = doc_ref.limit(options['limit'].to_i) + if options["limit"] + doc_ref = doc_ref.limit(options["limit"].to_i) end data = [] @@ -116,7 +118,7 @@ class FirestoreQueryService end def create_connection - credential_json = JSON.parse(source_options['gcp_key']) + credential_json = JSON.parse(source_options["gcp_key"]) Google::Cloud::Firestore.configure do |config| config.credentials = credential_json end diff --git a/app/services/google_oauth_service.rb b/app/services/google_oauth_service.rb index 8bfeb90cd9..70762eb57f 100644 --- a/app/services/google_oauth_service.rb +++ b/app/services/google_oauth_service.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + class GoogleOauthService def self.generate_base_auth_url - client_id = ENV.fetch('GOOGLE_CLIENT_ID', '') + client_id = ENV.fetch("GOOGLE_CLIENT_ID", "") "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=#{client_id}&redirect_uri=#{ENV.fetch('TOOLJET_HOST')}/oauth2/authorize" end def self.fetch_access_token(code) - access_token_url = 'https://oauth2.googleapis.com/token' - client_id = ENV.fetch('GOOGLE_CLIENT_ID', '') - client_secret = ENV.fetch('GOOGLE_CLIENT_SECRET', '') - grant_type = 'authorization_code' + access_token_url = "https://oauth2.googleapis.com/token" + client_id = ENV.fetch("GOOGLE_CLIENT_ID", "") + client_secret = ENV.fetch("GOOGLE_CLIENT_SECRET", "") + grant_type = "authorization_code" custom_params = [ %w[prompt consent], @@ -22,21 +24,21 @@ class GoogleOauthService grant_type: grant_type, redirect_uri: "#{ENV.fetch('TOOLJET_HOST')}/oauth2/authorize", **custom_params }.to_json, - headers: { 'Content-Type' => 'application/json' }) + headers: { "Content-Type" => "application/json" }) result = JSON.parse(response.body) - access_token = result['access_token'] - refresh_token = result['refresh_token'] + access_token = result["access_token"] + refresh_token = result["refresh_token"] - [['access_token', access_token], ['refresh_token', refresh_token]] + [["access_token", access_token], ["refresh_token", refresh_token]] end def self.refresh_access_token(refresh_token, data_source) - access_token_url = 'https://oauth2.googleapis.com/token' - client_id = ENV.fetch('GOOGLE_CLIENT_ID') - client_secret = ENV.fetch('GOOGLE_CLIENT_SECRET') - grant_type = 'refresh_token' + access_token_url = "https://oauth2.googleapis.com/token" + client_id = ENV.fetch("GOOGLE_CLIENT_ID") + client_secret = ENV.fetch("GOOGLE_CLIENT_SECRET") + grant_type = "refresh_token" response = HTTParty.post(access_token_url, body: { refresh_token: refresh_token, @@ -45,11 +47,11 @@ class GoogleOauthService grant_type: grant_type, redirect_uri: "#{ENV.fetch('TOOLJET_HOST')}/oauth2/authorize" }.to_json, - headers: { 'Content-Type' => 'application/json' }) + headers: { "Content-Type" => "application/json" }) result = JSON.parse(response.body) - access_token = result['access_token'] + access_token = result["access_token"] credential_id = data_source.options["access_token"]["credential_id"] credential = Credential.find(credential_id) diff --git a/app/services/googlesheets_query_service.rb b/app/services/googlesheets_query_service.rb index 342802ef3c..7767a6998b 100644 --- a/app/services/googlesheets_query_service.rb +++ b/app/services/googlesheets_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GooglesheetsQueryService attr_accessor :query, :source, :options, :source_options, :current_user @@ -10,12 +12,12 @@ class GooglesheetsQueryService end def process - operation = options['operation'] - access_token = source_options['access_token'] + operation = options["operation"] + access_token = source_options["access_token"] error = false - if operation === 'info' - spreadsheet_id = options['spreadsheet_id'] + if operation === "info" + spreadsheet_id = options["spreadsheet_id"] result = get_spreadsheet_info(spreadsheet_id, access_token) if result.code === 401 @@ -27,11 +29,11 @@ class GooglesheetsQueryService error = result.code != 200 end - if operation === 'append' + if operation === "append" - spreadsheet_id = options['spreadsheet_id'] - sheet = options['sheet'] - rows = options['rows'] + spreadsheet_id = options["spreadsheet_id"] + sheet = options["sheet"] + rows = options["rows"] result = append_data_to_sheet(spreadsheet_id, sheet, rows, access_token) @@ -60,7 +62,7 @@ class GooglesheetsQueryService error = result.code != 200 end - if operation === 'read' + if operation === "read" result = read_data(access_token) if result.code === 401 @@ -72,9 +74,9 @@ class GooglesheetsQueryService headers = [] values = [] - if result['values'] - headers = result['values'][0] if - values = result['values'][1..] if result['values'].size > 1 + if result["values"] + headers = result["values"][0] if + values = result["values"][1..] if result["values"].size > 1 end data = [] @@ -85,45 +87,44 @@ class GooglesheetsQueryService end data << row end - - else + + else error = true data = result["error"] end end if error - { status: 'error', code: 500, message: data["message"], data: data } + { status: "error", code: 500, message: data["message"], data: data } else - { status: 'success', data: data } + { status: "success", data: data } end end private def read_data_from_sheet(spreadsheet_id, sheet, access_token, range) - result = HTTParty.get("https://sheets.googleapis.com/v4/spreadsheets/#{spreadsheet_id}/values/#{sheet}!#{range}", - headers: { 'Content-Type': - 'application/json', "Authorization": "Bearer #{access_token}" }) + headers: { "Content-Type": + "application/json", "Authorization": "Bearer #{access_token}" }) result end def read_data(access_token) - spreadsheet_id = options['spreadsheet_id'] - sheet = options['sheet'] + spreadsheet_id = options["spreadsheet_id"] + sheet = options["sheet"] - read_data_from_sheet(spreadsheet_id, sheet, access_token, 'A1:V101') + read_data_from_sheet(spreadsheet_id, sheet, access_token, "A1:V101") end def append_data_to_sheet(spreadsheet_id, sheet, rows, access_token) - data = read_data_from_sheet(spreadsheet_id, sheet, access_token, 'A1:V1') - headers = data['values'][0] + data = read_data_from_sheet(spreadsheet_id, sheet, access_token, "A1:V1") + headers = data["values"][0] parsed_data = JSON.parse(rows) data_to_append = [] - + parsed_data.each do |row| row_data = [] headers.each_with_index do |header, index| @@ -136,8 +137,8 @@ class GooglesheetsQueryService "values": data_to_append }.to_json - result = HTTParty.post("https://sheets.googleapis.com/v4/spreadsheets/#{spreadsheet_id}/values/#{sheet}!A:V:append?valueInputOption=USER_ENTERED", body: data, headers: { 'Content-Type': - 'application/json', "Authorization": "Bearer #{access_token}" }) + result = HTTParty.post("https://sheets.googleapis.com/v4/spreadsheets/#{spreadsheet_id}/values/#{sheet}!A:V:append?valueInputOption=USER_ENTERED", body: data, headers: { "Content-Type": + "application/json", "Authorization": "Bearer #{access_token}" }) end def delete_row_from_sheet(spreadsheet_id, sheet, row_index, access_token) @@ -165,15 +166,14 @@ class GooglesheetsQueryService end def get_spreadsheet_info(spreadsheet_id, access_token) - result = HTTParty.get("https://sheets.googleapis.com/v4/spreadsheets/#{spreadsheet_id}", - headers: { 'Content-Type': - 'application/json', "Authorization": "Bearer #{access_token}" }) + headers: { "Content-Type": + "application/json", "Authorization": "Bearer #{access_token}" }) result end def refresh_access_token - GoogleOauthService.refresh_access_token(source_options['refresh_token'], @source ) + GoogleOauthService.refresh_access_token(source_options["refresh_token"], @source) end end diff --git a/app/services/graphql_query_service.rb b/app/services/graphql_query_service.rb index 41dd19d9f8..d57a59de1b 100644 --- a/app/services/graphql_query_service.rb +++ b/app/services/graphql_query_service.rb @@ -1,5 +1,6 @@ -class GraphqlQueryService +# frozen_string_literal: true +class GraphqlQueryService attr_accessor :data_query, :options, :source_options, :current_user, :data_source def initialize(data_query, data_source, options, source_options, current_user) @@ -11,12 +12,12 @@ class GraphqlQueryService end def process - url = source_options['url'] - method = options['method'] || 'GET' - source_headers = (source_options['headers'] || []).reject { |header| header[0].empty? }.to_h - url_params = source_options['url_params'] + url = source_options["url"] + method = options["method"] || "GET" + source_headers = (source_options["headers"] || []).reject { |header| header[0].empty? }.to_h + url_params = source_options["url_params"] encoded_url = url_encoded_with_params(url, url_params) - query = options['query'] + query = options["query"] client = Graphlient::Client.new(encoded_url, headers: source_headers) result = client.query(query) if result.errors.present? @@ -33,7 +34,7 @@ def url_encoded_with_params(original_url, url_params) original_url else uri = URI.parse(original_url) - params = URI.decode_www_form(uri.query || '') + url_params + params = URI.decode_www_form(uri.query || "") + url_params uri.query = URI.encode_www_form(params) uri.to_s end diff --git a/app/services/mongodb_query_service.rb b/app/services/mongodb_query_service.rb index 0a4f44f628..6ce4af7519 100644 --- a/app/services/mongodb_query_service.rb +++ b/app/services/mongodb_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MongodbQueryService attr_accessor :data_query, :data_source, :options, :source_options, :current_user @@ -9,17 +11,16 @@ class MongodbQueryService @current_user = current_user end - def self.connection options - - connection_type = options.dig('connection_type', 'value') + def self.connection(options) + connection_type = options.dig("connection_type", "value") if connection_type === "manual" - host = options.dig('host', 'value') - port = options.dig('port', 'value') - user = options.dig('username', 'value') - password = options.dig('password', 'value') - database = options.dig('database', 'value') + host = options.dig("host", "value") + port = options.dig("port", "value") + user = options.dig("username", "value") + password = options.dig("password", "value") + database = options.dig("database", "value") user = nil if user.blank? password = nil if password.blank? @@ -32,9 +33,9 @@ class MongodbQueryService password: password ) else - connection_string = options.dig('connection_string', 'value') + connection_string = options.dig("connection_string", "value") connection = Mongo::Client.new(connection_string, server_selection_timeout: 5) - end + end connection.collections end @@ -42,22 +43,22 @@ class MongodbQueryService def process error = nil data = [] - operation = options['operation'] + operation = options["operation"] begin if $connections.include? data_source.id connection = $connections[data_source.id][:connection] else - - if source_options['connection_type'] === 'manual' - password = source_options['password'] + + if source_options["connection_type"] === "manual" + password = source_options["password"] password = nil if password.blank? - user = source_options['username'] + user = source_options["username"] user = nil if user.blank? - host = source_options['host'] - port = source_options['port'] - database = source_options['database'] + host = source_options["host"] + port = source_options["port"] + database = source_options["database"] connection = Mongo::Client.new( [ "#{host}:#{port}" ], @@ -67,24 +68,24 @@ class MongodbQueryService password: password ) else - connection_string = source_options['connection_string'] + connection_string = source_options["connection_string"] connection = Mongo::Client.new(connection_string, server_selection_timeout: 5) end $connections[data_source.id] = { connection: connection } end - if operation === 'list_collections' + if operation === "list_collections" connection.collections.each { |coll| data << { name: coll.name } } end - if operation === 'insert_one' + if operation === "insert_one" collection = connection[options["collection"]] doc = JSON.parse(options["document"]) result = collection.insert_one(doc) end - if operation === 'insert_many' + if operation === "insert_many" collection = connection[options["collection"]] docs = JSON.parse(options["documents"]) result = collection.insert_many(docs) @@ -95,6 +96,6 @@ class MongodbQueryService error = e.message end - { status: error ? 'failed' : 'success', data: data, error: { message: error } } + { status: error ? "failed" : "success", data: data, error: { message: error } } end end diff --git a/app/services/mssql_query_service.rb b/app/services/mssql_query_service.rb index e19d48c0fb..ac6e4c182b 100644 --- a/app/services/mssql_query_service.rb +++ b/app/services/mssql_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MssqlQueryService include DatasourceUtils attr_accessor :data_query, :data_source, :options, :source_options, :current_user @@ -12,13 +14,13 @@ class MssqlQueryService 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'), + 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') + options.dig("azure", "value") ) || false ) end @@ -26,10 +28,10 @@ class MssqlQueryService def process connection = get_cached_connection(data_source) connection ||= create_connection - query_text = options['query'] + query_text = options["query"] results = connection.execute(query_text) - { status: 'success', data: results.to_a } + { status: "success", data: results.to_a } rescue StandardError => e if connection&.active? connection&.close @@ -43,13 +45,13 @@ class MssqlQueryService 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'], + 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'] + source_options["azure"] ) || false ) diff --git a/app/services/mysql_query_service.rb b/app/services/mysql_query_service.rb index 30e95f9c8c..c7ff52a3c8 100644 --- a/app/services/mysql_query_service.rb +++ b/app/services/mysql_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MysqlQueryService include DatasourceUtils attr_accessor :data_query, :data_source, :options, :source_options, :current_user @@ -12,11 +14,11 @@ class MysqlQueryService def self.connection options connection = Mysql2::Client.new( - database: options.dig('database', 'value'), - user: options.dig('username', 'value'), - password: options.dig('password', 'value'), - host: options.dig('host', 'value'), - port: options.dig('port', 'value'), + database: options.dig("database", "value"), + user: options.dig("username", "value"), + password: options.dig("password", "value"), + host: options.dig("host", "value"), + port: options.dig("port", "value"), ) end @@ -25,21 +27,21 @@ class MysqlQueryService connection = get_cached_connection(data_source) connection = create_connection unless connection - query_text = options['query'] + query_text = options["query"] results = connection.query(query_text) - { status: 'success', data: results.to_a } + { status: "success", data: results.to_a } end private def create_connection connection = Mysql2::Client.new( - host: source_options['host'], - username: source_options['username'], - password: source_options['password'], - port: source_options['port'], - database: source_options['database'] + host: source_options["host"], + username: source_options["username"], + password: source_options["password"], + port: source_options["port"], + database: source_options["database"] ) cache_connection(data_source, connection) diff --git a/app/services/postgresql_query_service.rb b/app/services/postgresql_query_service.rb index ca89efeae0..cdcbfa98e9 100644 --- a/app/services/postgresql_query_service.rb +++ b/app/services/postgresql_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PostgresqlQueryService include DatasourceUtils attr_accessor :data_query, :data_source, :options, :source_options, :current_user @@ -10,18 +12,17 @@ class PostgresqlQueryService @current_user = current_user end - def self.connection options + def self.connection(options) PG.connect( - dbname: options.dig('database', 'value'), - user: options.dig('username', 'value'), - password: options.dig('password', 'value'), - host: options.dig('host', 'value'), - port: options.dig('port', 'value'), + dbname: options.dig("database", "value"), + user: options.dig("username", "value"), + password: options.dig("password", "value"), + host: options.dig("host", "value"), + port: options.dig("port", "value"), ) end def process - error = false begin @@ -29,11 +30,11 @@ class PostgresqlQueryService connection = create_connection unless connection - query_text = '' - query_text = if options['mode'] === 'gui' + query_text = "" + query_text = if options["mode"] === "gui" send("generate_#{options['operation']}_query", options) else - options['query'] + options["query"] end result = connection.exec(query_text) @@ -45,24 +46,24 @@ class PostgresqlQueryService end puts e - error = { message: e.message } + error = { message: e.message } end if error - { status: 'error', code: 500, message: error[:message] } + { status: "error", code: 500, message: error[:message] } else - { status: 'success', data: result.to_a } + { status: "success", data: result.to_a } end end private def generate_bulk_update_pkey_query(options) - query_text = '' + query_text = "" - table_name = options['table'] - primary_key = options['primary_key_column'] - records = options['records'] + table_name = options["table"] + primary_key = options["primary_key_column"] + records = options["records"] records.each do |record| query_text = "#{query_text} UPDATE #{table_name} SET" @@ -78,17 +79,17 @@ class PostgresqlQueryService query_text end - def create_connection + def create_connection connection = PG.connect( - dbname: source_options['database'], - user: source_options['username'], - password: source_options['password'], - host: source_options['host'], - port: source_options['port'] + dbname: source_options["database"], + user: source_options["username"], + password: source_options["password"], + host: source_options["host"], + port: source_options["port"] ) connection.type_map_for_results = PG::BasicTypeMapForResults.new connection - + cache_connection(data_source, connection) connection diff --git a/app/services/query_service.rb b/app/services/query_service.rb index 74e0f9546f..c4ffed8fe3 100644 --- a/app/services/query_service.rb +++ b/app/services/query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class QueryService attr_accessor :data_query, :options, :current_user @@ -14,15 +16,15 @@ class QueryService data_source_options.keys.each do |key| option = data_source_options[key] - parsed_options[key] = if option['encrypted'] - Credential.find(option['credential_id']).value + parsed_options[key] = if option["encrypted"] + Credential.find(option["credential_id"]).value else - option['value'] + option["value"] end end if data_source query_options = data_query[:options] - if query_options.class.name === 'Hash' + if query_options.class.name === "Hash" parsed_query_options = get_query_options(query_options) else parsed_query_options = get_query_options(query_options.permit!.to_h) @@ -35,7 +37,6 @@ class QueryService private def get_query_options(object) - if object.is_a?(Hash) object.keys.each do |key| @@ -43,7 +44,7 @@ class QueryService end elsif object.class.name === "String" - if object.start_with?('{{') && object.end_with?('}}') + if object.start_with?("{{") && object.end_with?("}}") object = options[object] else variables = object.scan(/\{\{(.*?)\}\}/).to_a diff --git a/app/services/redis_query_service.rb b/app/services/redis_query_service.rb index 9f22a58060..f6b43238dc 100644 --- a/app/services/redis_query_service.rb +++ b/app/services/redis_query_service.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + class RedisQueryService - require 'redis' + require "redis" attr_accessor :data_query, :data_source, :options, :source_options, :current_user @@ -11,15 +13,14 @@ class RedisQueryService @current_user = current_user end - def self.connection options - - password = options.dig('password', 'value') + def self.connection(options) + password = options.dig("password", "value") password = nil if password.blank? connection = Redis.new( - host: options.dig('host', 'value'), - port: options.dig('port', 'value'), - user: options.dig('username', 'value'), + host: options.dig("host", "value"), + port: options.dig("port", "value"), + user: options.dig("username", "value"), password: password ) @@ -28,7 +29,7 @@ class RedisQueryService def process error = nil - password = source_options['password'] + password = source_options["password"] password = nil if password.blank? begin @@ -36,23 +37,23 @@ class RedisQueryService connection = $connections[data_source.id][:connection] else connection = Redis.new( - host: source_options['host'], - port: source_options['port'], - user: source_options['username'], + host: source_options["host"], + port: source_options["port"], + user: source_options["username"], password: password ) $connections[data_source.id] = { connection: connection } end - query_text = options['query'] + query_text = options["query"] - result = connection.call(query_text.split(' ')) + result = connection.call(query_text.split(" ")) rescue StandardError => e puts e error = e.message end - { status: error ? 'failed' : 'success', data: result, error: { message: error } } + { status: error ? "failed" : "success", data: result, error: { message: error } } end end diff --git a/app/services/restapi_query_service.rb b/app/services/restapi_query_service.rb index 44f3fbf235..8c16dd0144 100644 --- a/app/services/restapi_query_service.rb +++ b/app/services/restapi_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class RestapiQueryService attr_accessor :data_query, :options, :source_options, :current_user, :data_source @@ -10,31 +12,31 @@ class RestapiQueryService end def process - url = options['url'] + url = options["url"] if data_source url = "#{source_options['url']}#{url}" end - method = options['method'] || 'GET' - headers = (options['headers'] || []).reject { |header| header[0].empty? } + method = options["method"] || "GET" + headers = (options["headers"] || []).reject { |header| header[0].empty? } headers = headers.to_h - body = options['body'] - url_params = options['url_params'] + body = options["body"] + url_params = options["url_params"] - if source_options['auth_type'] === 'oauth2' + if source_options["auth_type"] === "oauth2" oauth_tokens = DataSourceUserOauth2.where(user: current_user, - data_source: data_source).order('created_at desc') + data_source: data_source).order("created_at desc") if oauth_tokens.size == 0 auth_url = "#{source_options['auth_url']}?response_type=code&client_id=#{source_options['client_id']}&redirect_uri=#{ENV.fetch('TOOLJET_HOST')}/oauth2/authorize&scope=#{source_options['scopes']}" - return { error: { message: 'needs authorization', code: 'oauth2_needs_auth', + return { error: { message: "needs authorization", code: "oauth2_needs_auth", data: { auth_url: auth_url } } } else - token = JSON.parse(oauth_tokens.first.options)['access_token'] + token = JSON.parse(oauth_tokens.first.options)["access_token"] end - if source_options['add_token_to'] === 'header' + if source_options["add_token_to"] === "header" headers = { **headers, 'Authorization': "Bearer #{token}" @@ -42,7 +44,7 @@ class RestapiQueryService end end - response = if method.downcase === 'get' + response = if method.downcase === "get" HTTParty.send(method.downcase, url, headers: headers, @@ -57,7 +59,7 @@ class RestapiQueryService if response.code == 401 auth_url = "#{source_options['auth_url']}?response_type=code&client_id=#{source_options['client_id']}&redirect_uri=#{ENV.fetch('TOOLJET_HOST')}/oauth2/authorize&scope=#{source_options['scopes']}" - return { error: { message: 'needs authorization', code: 'oauth2_needs_auth', + return { error: { message: "needs authorization", code: "oauth2_needs_auth", data: { auth_url: auth_url } } } end diff --git a/app/services/slack_oauth_service.rb b/app/services/slack_oauth_service.rb index dda5de013e..ce9b520f67 100644 --- a/app/services/slack_oauth_service.rb +++ b/app/services/slack_oauth_service.rb @@ -1,13 +1,15 @@ +# frozen_string_literal: true + class SlackOauthService def self.generate_base_auth_url - client_id = ENV.fetch('SLACK_CLIENT_ID') + client_id = ENV.fetch("SLACK_CLIENT_ID") "https://slack.com/oauth/v2/authorize?response_type=code&client_id=#{client_id}&redirect_uri=#{ENV.fetch('TOOLJET_HOST')}/oauth2/authorize" end def self.fetch_access_token(code) access_token_url = "https://slack.com/api/oauth.v2.access" - client_id = ENV.fetch('SLACK_CLIENT_ID') - client_secret = ENV.fetch('SLACK_CLIENT_SECRET') + client_id = ENV.fetch("SLACK_CLIENT_ID") + client_secret = ENV.fetch("SLACK_CLIENT_SECRET") data = { code: code, client_id: client_id, @@ -20,9 +22,9 @@ class SlackOauthService result = JSON.parse(response.body) - access_token = result['access_token'] - refresh_token = result['refresh_token'] + access_token = result["access_token"] + refresh_token = result["refresh_token"] - [['access_token', access_token], ['refresh_token', refresh_token]] + [["access_token", access_token], ["refresh_token", refresh_token]] end end diff --git a/app/services/slack_query_service.rb b/app/services/slack_query_service.rb index a3fd928d97..125a5df7f7 100644 --- a/app/services/slack_query_service.rb +++ b/app/services/slack_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SlackQueryService attr_accessor :query, :ource, :options, :source_options, :current_user @@ -10,34 +12,33 @@ class SlackQueryService end def process - operation = options['operation'] - access_token = source_options['access_token'] + operation = options["operation"] + access_token = source_options["access_token"] data = [] - if operation === 'list_users' + if operation === "list_users" result = HTTParty.get("https://slack.com/api/users.list", headers: { "Authorization": "Bearer #{access_token}" }) data = JSON.parse(result.body) end - if operation === 'send_message' + if operation === "send_message" body = { channel: options["channel"], text: options["message"], as_user: options["sendAsUser"] }.to_json - result = HTTParty.post("https://slack.com/api/chat.postMessage", body: body, headers: { "Content-Type": "application/json", "Authorization": "Bearer #{access_token}" } ) - data = JSON.parse(result.body) + data = JSON.parse(result.body) end - { status: 'success', data: data } + { status: "success", data: data } end end diff --git a/app/services/stripe_query_service.rb b/app/services/stripe_query_service.rb index f96f8f560b..f60f83f25f 100644 --- a/app/services/stripe_query_service.rb +++ b/app/services/stripe_query_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StripeQueryService attr_accessor :data_query, :options, :data_source, :source_options, :current_user @@ -18,22 +20,22 @@ class StripeQueryService end def process - stripe_api_key = source_options['api_key'] - api_base_url = 'https://api.stripe.com' - operation = options['operation'] - path = options['path'] + stripe_api_key = source_options["api_key"] + api_base_url = "https://api.stripe.com" + operation = options["operation"] + path = options["path"] url = "#{api_base_url}#{path}" # Replace path params in url with their values - path_params = options['params']['path'] - query_params = options['params']['query'] - body_params = options['params']['request'] + path_params = options["params"]["path"] + query_params = options["params"]["query"] + body_params = options["params"]["request"] url = replace_path_params(url, path_params) headers = { - 'Authorization': "Bearer #{stripe_api_key}" + "Authorization": "Bearer #{stripe_api_key}" } response = HTTParty.send( diff --git a/config.ru b/config.ru index ad1fbf295b..2e0308469b 100644 --- a/config.ru +++ b/config.ru @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # This file is used by Rack-based servers to start the application. -require_relative 'config/environment' +require_relative "config/environment" run Rails.application Rails.application.load_server diff --git a/config/application.rb b/config/application.rb index 621ad3193f..a3fb796034 100644 --- a/config/application.rb +++ b/config/application.rb @@ -19,7 +19,7 @@ require 'rails/test_unit/railtie' # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) -TOOLJET_VERSION = '0.5.12' +TOOLJET_VERSION = '0.5.13' module ToolJet class Application < Rails::Application diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb index 89d2efab2b..6d56e43900 100644 --- a/config/initializers/application_controller_renderer.rb +++ b/config/initializers/application_controller_renderer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # ActiveSupport::Reloader.to_prepare do diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb index d4d9b3b994..74f30e8875 100644 --- a/config/initializers/backtrace_silencers.rb +++ b/config/initializers/backtrace_silencers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. @@ -5,4 +7,4 @@ # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". -Rails.backtrace_cleaner.remove_silencers! if ENV['BACKTRACE'] +Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] diff --git a/config/initializers/connections.rb b/config/initializers/connections.rb index 9207a886c1..39f4ef8019 100644 --- a/config/initializers/connections.rb +++ b/config/initializers/connections.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + $connections = {} diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index 3b1c1b5ed1..82eafe5ca4 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Avoid CORS issues when API is called from the frontend app. diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 3e6969490d..3babc73f00 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. diff --git a/config/initializers/generators.rb b/config/initializers/generators.rb index 034fab6e56..3f6c501654 100644 --- a/config/initializers/generators.rb +++ b/config/initializers/generators.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.config.generators do |g| g.orm :active_record, primary_key_type: :uuid end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf9dc..dc84742212 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections diff --git a/config/initializers/lockbox.rb b/config/initializers/lockbox.rb index 368ac3a2d1..7f89ac64f0 100644 --- a/config/initializers/lockbox.rb +++ b/config/initializers/lockbox.rb @@ -1 +1,3 @@ -Lockbox.master_key = ENV.fetch('LOCKBOX_MASTER_KEY') +# frozen_string_literal: true + +Lockbox.master_key = ENV.fetch("LOCKBOX_MASTER_KEY") diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index dc1899682b..be6fedc535 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index bbfc3961bf..2f3c0db471 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # This file contains settings for ActionController::ParamsWrapper which diff --git a/config/puma.rb b/config/puma.rb index 6b7e304946..6439b9b138 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,28 +1,30 @@ +# frozen_string_literal: true + # Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -max_threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } -min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } +max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } threads min_threads_count, max_threads_count # Specifies the `worker_timeout` threshold that Puma will use to wait before # terminating a worker in development environments. # -worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' +worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch('PORT') { 3000 } +port ENV.fetch("PORT") { 3000 } # Specifies the `environment` that Puma will run in. # -environment ENV.fetch('RAILS_ENV') { 'development' } +environment ENV.fetch("RAILS_ENV") { "development" } # Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' } +pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together diff --git a/config/spring.rb b/config/spring.rb index 8f6432bf61..37a3543421 100644 --- a/config/spring.rb +++ b/config/spring.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + Spring.watch( - '.ruby-version', - '.rbenv-vars', - 'tmp/restart.txt', - 'tmp/caching-dev.txt' + ".ruby-version", + ".rbenv-vars", + "tmp/restart.txt", + "tmp/caching-dev.txt" ) diff --git a/db/seeds.rb b/db/seeds.rb index 141ca27390..d52804a054 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,5 @@ +# frozen_string_literal: true -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', status: 'active') +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", status: "active") diff --git a/deploy/ec2/setup_app b/deploy/ec2/setup_app index 19b4275b2c..1eb820e819 100755 --- a/deploy/ec2/setup_app +++ b/deploy/ec2/setup_app @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #!/usr/bin/env ruby def ensure_db_connectivity(user = ENV.fetch("PG_USER"), pass = ENV.fetch("PG_PASS"), host = ENV.fetch("PG_HOST")) @@ -17,7 +19,7 @@ def install_script_deps end def load_env - require 'dotenv' + require "dotenv" Dir.chdir "/home/ubuntu/app" Dotenv.load! Dotenv.require_keys("TOOLJET_HOST", "LOCKBOX_MASTER_KEY", "SECRET_KEY_BASE", "PG_DB", "PG_USER", "PG_HOST", "PG_PASS") diff --git a/docs/yarn.lock b/docs/yarn.lock index 77542ab238..e794c8220c 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2935,15 +2935,6 @@ cli-boxes@^2.2.1: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -clipboard@^2.0.0: - version "2.0.8" - resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba" - integrity sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ== - dependencies: - good-listener "^1.2.2" - select "^1.1.2" - tiny-emitter "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -3643,11 +3634,6 @@ del@^6.0.0: rimraf "^3.0.2" slash "^3.0.0" -delegate@^3.1.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" - integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== - depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -4605,13 +4591,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -good-listener@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" - integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= - dependencies: - delegate "^3.1.2" - got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7366,11 +7345,9 @@ prism-react-renderer@^1.1.1: integrity sha512-GHqzxLYImx1iKN1jJURcuRoA/0ygCcNhfGw1IT8nPIMzarmKQ3Nc+JcG0gi8JXQzuh0C5ShE4npMIoqNin40hg== prismjs@^1.23.0: - version "1.23.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33" - integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA== - optionalDependencies: - clipboard "^2.0.0" + version "1.24.1" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.24.1.tgz#c4d7895c4d6500289482fa8936d9cdd192684036" + integrity sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow== process-nextick-args@~2.0.0: version "2.0.1" @@ -8209,11 +8186,6 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= -select@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" - integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= - selfsigned@^1.10.8: version "1.10.8" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" @@ -8919,11 +8891,6 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= -tiny-emitter@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" - integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - tiny-invariant@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" diff --git a/frontend/assets/images/icons/day.svg b/frontend/assets/images/icons/day.svg new file mode 100644 index 0000000000..0a30f4df64 --- /dev/null +++ b/frontend/assets/images/icons/day.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/icons/night.svg b/frontend/assets/images/icons/night.svg new file mode 100644 index 0000000000..3c28b2f5f7 --- /dev/null +++ b/frontend/assets/images/icons/night.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App/App.jsx b/frontend/src/App/App.jsx index bcbe3509ea..9f04471d6e 100644 --- a/frontend/src/App/App.jsx +++ b/frontend/src/App/App.jsx @@ -24,7 +24,8 @@ class App extends React.Component { this.state = { currentUser: null, fetchedMetadata: false, - onboarded: true + onboarded: true, + darkMode: localStorage.getItem('darkMode') === 'true' }; } @@ -39,8 +40,13 @@ class App extends React.Component { history.push('/login'); } + switchDarkMode = (newMode) => { + this.setState({ darkMode: newMode }); + localStorage.setItem('darkMode', newMode); + } + render() { - const { currentUser, fetchedMetadata, updateAvailable, onboarded } = this.state; + const { currentUser, fetchedMetadata, updateAvailable, onboarded, darkMode } = this.state; if(currentUser && fetchedMetadata === false) { tooljetService.fetchMetaData().then((data) => { @@ -54,7 +60,7 @@ class App extends React.Component { return ( -
+
{updateAvailable &&

Update available

A new version of ToolJet has been released.

@@ -70,16 +76,16 @@ class App extends React.Component { - - + + - - - - + + + +
); diff --git a/frontend/src/Editor/Box.jsx b/frontend/src/Editor/Box.jsx index 3f849e3e1d..19d2f6253f 100644 --- a/frontend/src/Editor/Box.jsx +++ b/frontend/src/Editor/Box.jsx @@ -54,6 +54,7 @@ export const Box = function Box({ paramUpdated, changeCanDrag, containerProps, + darkMode }) { const backgroundColor = yellow ? 'yellow' : ''; @@ -91,6 +92,7 @@ export const Box = function Box({ height={height} component={component} containerProps={containerProps} + darkMode={darkMode} > ) : (
diff --git a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx index a8960b78e4..762fee1d7d 100644 --- a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx +++ b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx @@ -8,7 +8,8 @@ import 'codemirror/addon/display/placeholder'; import 'codemirror/addon/search/match-highlighter'; import 'codemirror/addon/hint/show-hint.css'; import 'codemirror/theme/base16-light.css'; -import 'codemirror/theme/duotone-light.css'; +import 'codemirror/theme/duotone-light.css' +import 'codemirror/theme/monokai.css'; import { getSuggestionKeys, onBeforeChange, handleChange } from './utils'; import { resolveReferences } from '@/_helpers/utils'; @@ -25,6 +26,7 @@ export function CodeHinter({ enablePreview, height }) { + console.log('theme', theme) const options = { lineNumbers: lineNumbers, singleLine: true, diff --git a/frontend/src/Editor/Components/Chart.jsx b/frontend/src/Editor/Components/Chart.jsx index e4d1e22c84..417e71ec22 100644 --- a/frontend/src/Editor/Components/Chart.jsx +++ b/frontend/src/Editor/Components/Chart.jsx @@ -9,7 +9,7 @@ const Plot = createPlotlyComponent(Plotly) import Skeleton from 'react-loading-skeleton'; export const Chart = function Chart({ - id, width, height, component, onComponentClick, currentState + id, width, height, component, onComponentClick, currentState, darkMode }) { console.log('currentState', currentState); @@ -27,7 +27,7 @@ export const Chart = function Chart({ const computedStyles = { width, height, - backgroundColor: 'white' + background: darkMode ? '#1f2936' : 'white' }; const dataProperty = component.definition.properties.data; @@ -48,14 +48,23 @@ export const Chart = function Chart({ const layout = { width, height, - title, + plot_bgcolor: darkMode ? '#1f2936' : null, + paper_bgcolor: darkMode ? '#1f2936' : null, + title: { + text: title, + font: { + color: darkMode ? '#c3c3c3' : null + } + }, xaxis: { showgrid: showGridLines, - showline: true + showline: true, + color: darkMode ? '#c3c3c3' : null }, yaxis: { showgrid: showGridLines, - showline: true + showline: true, + color: darkMode ? '#c3c3c3' : null } } @@ -101,7 +110,9 @@ export const Chart = function Chart({ > {loadingState === true ?
- +
+
+
: Search:{' '} { handleSearchTextChange(e.target.value) @@ -494,7 +496,7 @@ export function Table({ return (
onComponentClick(id, component)} > @@ -587,7 +589,9 @@ export function Table({ {loadingState === true && (
- +
+
+
)}
diff --git a/frontend/src/Editor/Components/Text.jsx b/frontend/src/Editor/Components/Text.jsx index 2894709264..533e5def9c 100644 --- a/frontend/src/Editor/Components/Text.jsx +++ b/frontend/src/Editor/Components/Text.jsx @@ -45,7 +45,7 @@ export const Text = function Text({ {!loadingState &&
} {loadingState === true && (
- +
)}
diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx index 4482de5ee6..83feddf796 100644 --- a/frontend/src/Editor/Container.jsx +++ b/frontend/src/Editor/Container.jsx @@ -30,7 +30,8 @@ export const Container = ({ removeComponent, deviceWindowWidth, scaleValue, - selectedComponent + selectedComponent, + darkMode }) => { const styles = { @@ -278,6 +279,7 @@ export const Container = ({ scaleValue={scaleValue} deviceWindowWidth={deviceWindowWidth} isSelectedComponent={selectedComponent? selectedComponent.id === key : false} + darkMode={darkMode} containerProps={{ mode, snapToGrid, @@ -295,7 +297,8 @@ export const Container = ({ currentLayout, scaleValue, deviceWindowWidth, - selectedComponent + selectedComponent, + darkMode }} /> } diff --git a/frontend/src/Editor/DataSourceManager/DataSourceManager.jsx b/frontend/src/Editor/DataSourceManager/DataSourceManager.jsx index ee9e0719bd..db597e2f6b 100644 --- a/frontend/src/Editor/DataSourceManager/DataSourceManager.jsx +++ b/frontend/src/Editor/DataSourceManager/DataSourceManager.jsx @@ -140,8 +140,8 @@ class DataSourceManager extends React.Component { size={selectedDataSource ? 'lg' : 'xl'} onEscapeKeyDown={this.hideModal} className="mt-5" + contentClassName={this.props.darkMode ? 'theme-dark' : ''} animation={false} - backdrop="static" > @@ -175,7 +175,7 @@ class DataSourceManager extends React.Component { )} - @@ -235,7 +235,7 @@ class DataSourceManager extends React.Component {
Please white-list our IP address if your datasource is not publicly accessible. - IP: {config.SERVER_IP} + IP: {config.SERVER_IP} toast.success('IP copied to clipboard', { @@ -244,7 +244,7 @@ class DataSourceManager extends React.Component { }) } > - +
diff --git a/frontend/src/Editor/DraggableBox.jsx b/frontend/src/Editor/DraggableBox.jsx index 5274984260..5f618aca3c 100644 --- a/frontend/src/Editor/DraggableBox.jsx +++ b/frontend/src/Editor/DraggableBox.jsx @@ -84,6 +84,7 @@ export const DraggableBox = function DraggableBox({ scaleValue, deviceWindowWidth, isSelectedComponent, + darkMode }) { const [isResizing, setResizing] = useState(false); const [canDrag, setCanDrag] = useState(true); @@ -230,6 +231,7 @@ export const DraggableBox = function DraggableBox({ onComponentClick={onComponentClick} currentState={currentState} containerProps={containerProps} + darkMode={darkMode} />
@@ -247,6 +249,7 @@ export const DraggableBox = function DraggableBox({ onComponentOptionsChanged={onComponentOptionsChanged} onComponentClick={onComponentClick} currentState={currentState} + darkMode={darkMode} />
)} diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index 129cacfc97..44edc5df16 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { datasourceService, dataqueryService, appService, authenticationService } from '@/_services'; +import { DarkModeToggle } from '@/_components/DarkModeToggle'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { Container } from './Container'; @@ -571,11 +572,18 @@ class Editor extends React.Component {
+
+ +
{app.id && }
@@ -590,6 +598,7 @@ class Editor extends React.Component { appName={app.name} appDefinition={appDefinition} app={app} + darkMode={this.props.darkMode} onVersionDeploy={this.onVersionDeploy} /> )} @@ -606,7 +615,6 @@ class Editor extends React.Component { display: 'flex', alignItems: 'center', justifyContent: 'center', - background: '#f0f0f0', zIndex: '200' }} maxWidth={showLeftSidebar ? '30%' : '0%'} @@ -621,6 +629,7 @@ class Editor extends React.Component {
this.setState({ showDataSourceManagerModal: false })} dataSourcesChanged={this.dataSourcesChanged} showDataSourceManagerModal={this.state.showDataSourceManagerModal} @@ -725,6 +737,7 @@ class Editor extends React.Component { appDefinition={appDefinition} appDefinitionChanged={this.appDefinitionChanged} snapToGrid={true} + darkMode={this.props.darkMode} mode={'edit'} zoomLevel={zoomLevel} currentLayout={currentLayout} @@ -838,6 +851,7 @@ class Editor extends React.Component { editingQuery={editingQuery} queryPaneHeight={queryPaneHeight} currentState={currentState} + darkMode={this.props.darkMode} />
@@ -849,33 +863,7 @@ class Editor extends React.Component {
- +
@@ -891,7 +879,9 @@ class Editor extends React.Component { currentState={currentState} allComponents={appDefinition.components} key={selectedComponent.id} + switchSidebarTab={this.switchSidebarTab} apps={apps} + darkMode={this.props.darkMode} > ) : (
Please select a component to inspect
diff --git a/frontend/src/Editor/Inspector/Components/Chart.jsx b/frontend/src/Editor/Inspector/Components/Chart.jsx index c214c3f994..173a43b33f 100644 --- a/frontend/src/Editor/Inspector/Components/Chart.jsx +++ b/frontend/src/Editor/Inspector/Components/Chart.jsx @@ -72,7 +72,7 @@ class Chart extends React.Component { -
-
+
+
{action.buttonText}
@@ -355,13 +355,14 @@ class Table extends React.Component {
{columns.value.map((item, index) => ( -
+
-
+
{ const initialValue = definition ? definition.value : ''; const paramMeta = componentMeta[paramType][param.name]; @@ -22,7 +22,7 @@ export const Code = ({ currentState={currentState} initialValue={initialValue} mode={options.mode} - theme={options.theme} + theme={darkMode? 'monokai' : options.theme} className={options.className} onChange={(value) => handleCodeChanged(value)} /> diff --git a/frontend/src/Editor/Inspector/Inspector.jsx b/frontend/src/Editor/Inspector/Inspector.jsx index 1c8381d563..089178b1b8 100644 --- a/frontend/src/Editor/Inspector/Inspector.jsx +++ b/frontend/src/Editor/Inspector/Inspector.jsx @@ -17,7 +17,9 @@ export const Inspector = ({ allComponents, componentChanged, currentState, - apps + apps, + darkMode, + switchSidebarTab }) => { const selectedComponent = { id: selectedComponentId, component: allComponents[selectedComponentId].component, layouts: allComponents[selectedComponentId].layouts} @@ -153,7 +155,7 @@ export const Inspector = ({ return (
-
+
-
- - {/* brrr */} - -
- -

- -
-
- - } - > - -
+
+
@@ -208,6 +190,7 @@ export const Inspector = ({ eventOptionUpdated={eventOptionUpdated} components={components} currentState={currentState} + darkMode={darkMode} /> } @@ -221,19 +204,19 @@ export const Inspector = ({ eventOptionUpdated={eventOptionUpdated} components={components} currentState={currentState} + darkMode={darkMode} /> } {!['Table', 'Chart'].includes(componentMeta.component) &&
- {Object.keys(componentMeta.properties).map((property) => renderElement(component, componentMeta, paramUpdated, dataQueries, property, 'properties', currentState, components))} -
Style
+ {Object.keys(componentMeta.properties).map((property) => renderElement(component, componentMeta, paramUpdated, dataQueries, property, 'properties', currentState, components, darkMode))} + + {Object.keys(componentMeta.styles).length > 0 &&
Style
} {Object.keys(componentMeta.styles).map((style) => renderElement(component, componentMeta, paramUpdated, dataQueries, style, 'styles', currentState, components))} -
Events
- {Object.keys(componentMeta.events).map((eventName) => renderEvent(component, eventUpdated, dataQueries, eventOptionUpdated, eventName, componentMeta.events[eventName], currentState, components, apps))} - - + {Object.keys(componentMeta.events).length > 0 &&
Events
} + {Object.keys(componentMeta.events).map((eventName) => renderEvent(component, eventUpdated, dataQueries, eventOptionUpdated, eventName, componentMeta.events[eventName], currentState, components, apps))}
} diff --git a/frontend/src/Editor/Inspector/Utils.js b/frontend/src/Editor/Inspector/Utils.js index 915a360d8d..88655210fb 100644 --- a/frontend/src/Editor/Inspector/Utils.js +++ b/frontend/src/Editor/Inspector/Utils.js @@ -31,7 +31,7 @@ export function renderQuerySelector(component, dataQueries, eventOptionUpdated, />) } -export function renderElement(component, componentMeta, paramUpdated, dataQueries, param, paramType, currentState, components = {}) { +export function renderElement(component, componentMeta, paramUpdated, dataQueries, param, paramType, currentState, components = {}, darkMode = false) { const definition = component.component.definition[paramType][param]; const meta = componentMeta[paramType][param]; console.log('definition', definition); @@ -47,6 +47,7 @@ export function renderElement(component, componentMeta, paramUpdated, dataQuerie components={components} componentMeta={componentMeta} currentState={currentState} + darkMode={darkMode} /> ); } diff --git a/frontend/src/Editor/ManageAppUsers.jsx b/frontend/src/Editor/ManageAppUsers.jsx index ba23501a0b..5873b6e869 100644 --- a/frontend/src/Editor/ManageAppUsers.jsx +++ b/frontend/src/Editor/ManageAppUsers.jsx @@ -147,11 +147,21 @@ class ManageAppUsers extends React.Component { Share - + Users and permissions
-
@@ -203,7 +213,7 @@ class ManageAppUsers extends React.Component { }) } > - +
{slugError}
@@ -257,7 +267,7 @@ class ManageAppUsers extends React.Component {
- +
diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Airtable.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Airtable.jsx index 870648d5a9..93ceeb4958 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Airtable.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Airtable.jsx @@ -65,6 +65,7 @@ class Airtable extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.base_id} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'base_id', value)} /> @@ -74,6 +75,7 @@ class Airtable extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.table_name} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'table_name', value)} /> @@ -83,6 +85,7 @@ class Airtable extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.page_size} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'page_size', value)} /> @@ -92,6 +95,7 @@ class Airtable extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.offset} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'offset', value)} /> @@ -106,6 +110,7 @@ class Airtable extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.base_id} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'base_id', value)} /> @@ -115,6 +120,7 @@ class Airtable extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.table_name} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'table_name', value)} /> @@ -124,6 +130,7 @@ class Airtable extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.record_id} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'record_id', value)} /> diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Dynamodb.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Dynamodb.jsx index c601b3c8d3..cf70f77f56 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Dynamodb.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Dynamodb.jsx @@ -67,9 +67,9 @@ class Dynamodb extends React.Component { changeOption(this, 'query_condition', value)} /> @@ -84,9 +84,9 @@ class Dynamodb extends React.Component { changeOption(this, 'scan_condition', value)} /> @@ -101,6 +101,7 @@ class Dynamodb extends React.Component { changeOption(this, 'table', value)} /> @@ -110,7 +111,7 @@ class Dynamodb extends React.Component { changeOption(this, 'index', value)} /> @@ -75,6 +76,7 @@ class Elasticsearch extends React.Component { changeOption(this, 'id', value)} /> @@ -85,7 +87,7 @@ class Elasticsearch extends React.Component { initialValue={options.body} mode="javascript" placeholder={'{ doc: { page_count: 225 } }'} - theme="duotone-light" + theme={this.props.darkMode ? 'monokai' : 'duotone-light'} lineNumbers={true} className="query-hinter" onChange={(value) => changeOption(this, 'body', value)} @@ -101,6 +103,7 @@ class Elasticsearch extends React.Component { changeOption(this, 'index', value)} /> @@ -109,6 +112,7 @@ class Elasticsearch extends React.Component { changeOption(this, 'id', value)} /> @@ -122,6 +126,7 @@ class Elasticsearch extends React.Component { changeOption(this, 'index', value)} /> @@ -132,7 +137,7 @@ class Elasticsearch extends React.Component { initialValue={options.body} mode="javascript" placeholder={'{ "name": "The Hitchhikers Guide to the Galaxy" }'} - theme="duotone-light" + theme={this.props.darkMode ? 'monokai' : 'duotone-light'} lineNumbers={true} className="query-hinter" onChange={(value) => changeOption(this, 'body', value)} @@ -147,6 +152,7 @@ class Elasticsearch extends React.Component { changeOption(this, 'index', value)} /> @@ -158,7 +164,7 @@ class Elasticsearch extends React.Component { initialValue={options.query} mode="sql" placeholder={'{ "name": "" }'} - theme="duotone-light" + theme={this.props.darkMode ? 'monokai' : 'duotone-light'} lineNumbers={true} className="query-hinter" onChange={(value) => changeOption(this, 'query', value)} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Firestore.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Firestore.jsx index aa57f613e2..e85d680d52 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Firestore.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Firestore.jsx @@ -75,13 +75,14 @@ class Firestore extends React.Component { placeholder="Select.." /> - {this.state.options.operation === 'get_document' || this.state.options.operation === 'delete_document' && ( + {(this.state.options.operation === 'get_document' || this.state.options.operation === 'delete_document') && (
changeOption(this, 'path', value)} />
@@ -95,6 +96,7 @@ class Firestore extends React.Component { changeOption(this, 'path', value)} />
@@ -106,6 +108,7 @@ class Firestore extends React.Component { theme="duotone-light" lineNumbers={true} className="query-hinter" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'body', value)} /> @@ -118,6 +121,7 @@ class Firestore extends React.Component { changeOption(this, 'collection', value)} /> @@ -126,6 +130,7 @@ class Firestore extends React.Component { changeOption(this, 'document_id_key', value)} /> @@ -138,7 +143,7 @@ class Firestore extends React.Component { onChange={(instance) => changeOption(this, 'records', instance.getValue())} placeholder="{ }" options={{ - theme: 'duotone-light', + theme: this.props.darkMode ? 'monokai' : 'default', mode: 'javascript', lineWrapping: true, scrollbarStyle: null @@ -154,6 +159,7 @@ class Firestore extends React.Component { changeOption(this, 'path', value)} /> @@ -164,6 +170,7 @@ class Firestore extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.order} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'order', value)} /> @@ -173,6 +180,7 @@ class Firestore extends React.Component { currentState={this.props.currentState} initialValue={this.state.options.limit} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'limit', value)} /> @@ -183,6 +191,7 @@ class Firestore extends React.Component { changeOption(this, 'where_field', value)} /> @@ -214,6 +223,7 @@ class Firestore extends React.Component { changeOption(this, 'where_value', value)} /> diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Googlesheets.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Googlesheets.jsx index 86a8514a42..a95fbd3d34 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Googlesheets.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Googlesheets.jsx @@ -110,7 +110,7 @@ class Googlesheets extends React.Component { changeOption(this, 'rows', value)} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Graphql.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Graphql.jsx index 64852e001c..97b628023f 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Graphql.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Graphql.jsx @@ -26,7 +26,7 @@ class Graphql extends React.Component { currentState={this.props.currentState} initialValue={options.query} mode="sql" - theme="duotone-light" + theme={this.props.darkMode ? 'monokai' : 'duotone-light'} lineNumbers={true} className="query-hinter" onChange={(value) => changeOption(this, 'query', value)} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Mongodb.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Mongodb.jsx index 6761d1c8d4..66527267a5 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Mongodb.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Mongodb.jsx @@ -80,6 +80,7 @@ class Mongodb extends React.Component { changeOption(this, 'collection', value)} /> @@ -89,7 +90,7 @@ class Mongodb extends React.Component { changeOption(this, 'collection', value)} /> @@ -116,7 +118,7 @@ class Mongodb extends React.Component { changeOption(this, 'query', value)} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Mysql.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Mysql.jsx index 1d4bb3305c..06495dc230 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Mysql.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Mysql.jsx @@ -28,7 +28,7 @@ class Mysql extends React.Component { currentState={this.props.currentState} initialValue={options.query} mode="sql" - theme="duotone-light" + theme={this.props.darkMode ? 'monokai' : 'duotone-light'} lineNumbers={true} className="query-hinter" onChange={(value) => changeOption(this, 'query', value)} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Postgresql.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Postgresql.jsx index 04beb54a26..76ae998c62 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Postgresql.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Postgresql.jsx @@ -49,7 +49,7 @@ class Postgresql extends React.Component { currentState={this.props.currentState} initialValue={options.query} mode="sql" - theme="duotone-light" + theme={this.props.darkMode ? 'monokai' : 'duotone-light'} lineNumbers={true} className="query-hinter" enablePreview diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Redis.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Redis.jsx index d02a188407..3b752af747 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Redis.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Redis.jsx @@ -33,7 +33,7 @@ class Redis extends React.Component { onChange={(instance) => changeOption(this, 'query', instance.getValue())} placeholder="PING" options={{ - theme: 'duotone-light', + theme: this.props.darkMode ? 'monokai' : 'duotone-light', mode: 'sql', lineWrapping: true, scrollbarStyle: null diff --git a/frontend/src/Editor/QueryManager/QueryEditors/Restapi.jsx b/frontend/src/Editor/QueryManager/QueryEditors/Restapi.jsx index 709fa3f10a..3888b5afa4 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/Restapi.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/Restapi.jsx @@ -95,6 +95,7 @@ class Restapi extends React.Component { currentState={this.props.currentState} initialValue={options.url} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => { changeOption(this, 'url', value); }} @@ -119,6 +120,7 @@ class Restapi extends React.Component { this.keyValuePairValueChanged(value, 0, option.value, index)} /> @@ -126,6 +128,7 @@ class Restapi extends React.Component { currentState={this.props.currentState} className="form-control codehinter-query-editor-input" initialValue={pair[1]} + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => this.keyValuePairValueChanged(value, 1, option.value, index)} /> changeOption(this, 'channel', value)} /> @@ -91,6 +92,7 @@ class Slack extends React.Component { currentState={this.props.currentState} initialValue={options.message} className="codehinter-query-editor-input" + theme={this.props.darkMode ? 'monokai' : 'default'} onChange={(value) => changeOption(this, 'message', value)} /> diff --git a/frontend/src/Editor/QueryManager/QueryManager.jsx b/frontend/src/Editor/QueryManager/QueryManager.jsx index 9d933a0a33..351e59110b 100644 --- a/frontend/src/Editor/QueryManager/QueryManager.jsx +++ b/frontend/src/Editor/QueryManager/QueryManager.jsx @@ -255,7 +255,7 @@ class QueryManager extends React.Component { autoFocus={false} /> - + @@ -278,7 +278,7 @@ class QueryManager extends React.Component { this.previewPanelRef.current.scrollIntoView(); }) .catch(({ error, data }) => { - debugger; + }); }} className={`btn btn-secondary m-1 float-right1 ${previewLoading ? ' btn-loading' : ''}`} @@ -348,6 +348,7 @@ class QueryManager extends React.Component { options={this.state.options} optionsChanged={this.optionsChanged} currentState={currentState} + darkMode={this.props.darkMode} />
@@ -355,6 +356,7 @@ class QueryManager extends React.Component { changeOption={this.optionchanged} options={this.state.options} currentState={currentState} + darkMode={this.props.darkMode} />
@@ -373,6 +375,7 @@ class QueryManager extends React.Component { style={{ fontSize: '0.7rem' }} enableClipboard={false} src={queryPreviewData} + theme={this.props.darkMode ? 'shapeshifter' : 'rjv-default'} displayDataTypes={true} collapsed={false} displayObjectSize={true} diff --git a/frontend/src/Editor/QueryManager/Transformation.jsx b/frontend/src/Editor/QueryManager/Transformation.jsx index 5cdd9437d7..858aa3c490 100644 --- a/frontend/src/Editor/QueryManager/Transformation.jsx +++ b/frontend/src/Editor/QueryManager/Transformation.jsx @@ -8,7 +8,7 @@ import 'codemirror/addon/search/match-highlighter'; import 'codemirror/addon/hint/show-hint.css'; import { CodeHinter } from '../CodeBuilder/CodeHinter'; -export const Transformation = ({ changeOption, options, currentState }) => { +export const Transformation = ({ changeOption, options, currentState, darkMode }) => { const defaultValue = options.transformation || `// write your code here // return value will be set as data and the original data will be available as rawData @@ -51,7 +51,7 @@ return data.filter(row => row.amount > 1000);`; currentState={currentState} initialValue={value} mode="javascript" - theme="base16-light" + theme={darkMode? 'monokai' : 'base16-light'} lineNumbers={true} className="query-hinter" ignoreBraces={true} diff --git a/frontend/src/Editor/QueryManager/constants.js b/frontend/src/Editor/QueryManager/constants.js index 853babcfa4..97499790d4 100644 --- a/frontend/src/Editor/QueryManager/constants.js +++ b/frontend/src/Editor/QueryManager/constants.js @@ -1,15 +1,19 @@ export const defaultOptions = { - postgresql: {}, + postgresql: { + mode: 'sql' + }, redis: { query: 'PING' }, mysql: {}, graphql: {}, firestore: { - path: '' + path: '', + operation: 'get_document' }, elasticsearch: { - query: '' + query: '', + operation: 'search' }, restapi: { method: 'GET', diff --git a/frontend/src/Editor/SaveAndPreview.jsx b/frontend/src/Editor/SaveAndPreview.jsx index b0283b3c85..c5c2dc6877 100644 --- a/frontend/src/Editor/SaveAndPreview.jsx +++ b/frontend/src/Editor/SaveAndPreview.jsx @@ -91,6 +91,7 @@ class SaveAndPreview extends React.Component { enforceFocus={false} animation={false} onEscapeKeyDown={() => this.hideModal()} + contentClassName={this.props.darkMode ? 'theme-dark' : ''} > Versions and deployments @@ -101,7 +102,7 @@ class SaveAndPreview extends React.Component { )} -
diff --git a/frontend/src/Editor/Viewer.jsx b/frontend/src/Editor/Viewer.jsx index 18681e84c0..36c0e4287f 100644 --- a/frontend/src/Editor/Viewer.jsx +++ b/frontend/src/Editor/Viewer.jsx @@ -15,6 +15,7 @@ import { runQuery } from '@/_helpers/appUtils'; import queryString from 'query-string'; +import { DarkModeToggle } from '@/_components/DarkModeToggle'; class Viewer extends React.Component { constructor(props) { @@ -137,7 +138,12 @@ class Viewer extends React.Component { {this.state.app && {this.state.app.name}} -
+
+ +
@@ -150,6 +156,7 @@ class Viewer extends React.Component { appDefinitionChanged={() => false} // function not relevant in viewer snapToGrid={true} appLoading={isLoading} + darkMode={this.props.darkMode} onEvent={(eventName, options) => onEvent(this, eventName, options, 'view')} mode="view" scaleValue={scaleValue} diff --git a/frontend/src/HomePage/AppMenu.jsx b/frontend/src/HomePage/AppMenu.jsx index d08f232acf..eb12621175 100644 --- a/frontend/src/HomePage/AppMenu.jsx +++ b/frontend/src/HomePage/AppMenu.jsx @@ -89,7 +89,7 @@ export const AppMenu = function AppMenu({ } > - + } \ No newline at end of file diff --git a/frontend/src/HomePage/Folders.jsx b/frontend/src/HomePage/Folders.jsx index c72ff6429b..c00054ec0a 100644 --- a/frontend/src/HomePage/Folders.jsx +++ b/frontend/src/HomePage/Folders.jsx @@ -36,7 +36,7 @@ export const Folders = function Folders({ folderChanged(folder); } - return (
+ return (
{isLoading && (
{[1,2,3,4, 5].map(element => { diff --git a/frontend/src/HomePage/HomePage.jsx b/frontend/src/HomePage/HomePage.jsx index 72825687d9..4d75968608 100644 --- a/frontend/src/HomePage/HomePage.jsx +++ b/frontend/src/HomePage/HomePage.jsx @@ -125,7 +125,8 @@ class HomePage extends React.Component { />
{!isLoading && meta.total_count === 0 &&
-
+
Name
+ className={`table table-vcenter ${this.props.darkMode ? 'bg-dark' : 'bg-white' }`}> {isLoading && ( <> @@ -196,7 +197,7 @@ class HomePage extends React.Component { @@ -295,7 +298,7 @@ class ManageOrgUsers extends React.Component { - {user.status} + {user.status}
{app.name}
- created {app.created_at} ago by {app.user.first_name} {app.user.last_name} + created {app.created_at} ago by {app.user.first_name} {app.user.last_name}
-
+
@@ -269,7 +272,7 @@ class ManageOrgUsers extends React.Component {
- + {user.email} {archivingUser === null && ( diff --git a/frontend/src/_components/DarkModeToggle.jsx b/frontend/src/_components/DarkModeToggle.jsx new file mode 100644 index 0000000000..7ad47f28d8 --- /dev/null +++ b/frontend/src/_components/DarkModeToggle.jsx @@ -0,0 +1,22 @@ +import React, { useState, useEffect } from 'react'; + +export const DarkModeToggle = function DarkModeToggle({ + darkMode, switchDarkMode +}) { + + const [darkModeEnabled, setMode] = useState(darkMode); + + const icon = darkModeEnabled ? 'night.svg' : 'day.svg'; + + return
+ +
+} diff --git a/frontend/src/_components/Header.jsx b/frontend/src/_components/Header.jsx index f7dced7098..8a49bf7420 100644 --- a/frontend/src/_components/Header.jsx +++ b/frontend/src/_components/Header.jsx @@ -2,9 +2,10 @@ import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { authenticationService } from '@/_services'; import { history } from '@/_helpers'; +import { DarkModeToggle } from './DarkModeToggle'; export const Header = function Header({ - + switchDarkMode, darkMode }) { const [pahtName, setPathName] = useState(document.location.pathname); @@ -35,7 +36,7 @@ export const Header = function Header({
  • - + Apps @@ -46,7 +47,7 @@ export const Header = function Header({
  • - + Users @@ -55,6 +56,12 @@ export const Header = function Header({
  • +
    + +
    ( +export const PrivateRoute = ({ component: Component, switchDarkMode, darkMode, ...rest }) => ( { @@ -14,7 +14,7 @@ export const PrivateRoute = ({ component: Component, ...rest }) => ( } // authorised so return component - return ; + return ; }} /> ); diff --git a/frontend/src/_components/index.js b/frontend/src/_components/index.js index 9d510e39da..b443596f81 100644 --- a/frontend/src/_components/index.js +++ b/frontend/src/_components/index.js @@ -2,3 +2,4 @@ export * from './PrivateRoute'; export * from './Pagination'; export * from './Header'; export * from './ConfirmDialog'; +export * from './DarkModeToggle'; diff --git a/frontend/src/_services/authentication.service.js b/frontend/src/_services/authentication.service.js index d78cdef8dd..a44a7728dd 100644 --- a/frontend/src/_services/authentication.service.js +++ b/frontend/src/_services/authentication.service.js @@ -16,7 +16,7 @@ export const authenticationService = { function login(email, password) { const requestOptions = { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }) }; @@ -34,7 +34,7 @@ function login(email, password) { function signup(email) { const requestOptions = { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email }) }; diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss index 065bb97958..78003bc514 100644 --- a/frontend/src/_styles/theme.scss +++ b/frontend/src/_styles/theme.scss @@ -155,7 +155,7 @@ body { } .header { border: solid rgba(0, 0, 0, 0.125); - border-width: 0px 0px 2px 0px; + border-width: 0px 0px 1px 0px; height: 40px; .component-name { @@ -364,10 +364,11 @@ body { .header { border: solid rgba(0, 0, 0, 0.125); border-width: 0px 0px 1px 0px; + background: white; position: fixed; z-index: 2; - background: white; width: 54%; + margin-top: 0px; } .preview-header { @@ -1641,3 +1642,328 @@ input:focus-visible { border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; } + +.theme-dark { + .navbar .navbar-nav .active > .nav-link, .theme-dark .navbar .navbar-nav .nav-link.active, .theme-dark .navbar .navbar-nav .nav-link.show, .theme-dark .navbar .navbar-nav .show > .nav-link { + color: #fff; + } + + .card-body > :last-child { + color: #c3c3c3!important; + } + + .form-control { + border: 1px solid #324156; + } + + .card { + background-color: #2f3c4c!important; + } + + .DateInput { + background: #1f2936 + } + + .DateInput_input { + background-color: #1f2936; + } + + .DateRangePickerInput { + background-color: #1f2936 + } + + .DateInput_input__focused { + background: #1f2936; + } + + .DateRangePickerInput__withBorder { + border: 1px solid #1f2936; + } + + .main .canvas-container .canvas-area { + background: #2f3c4c; + } + + .rdtOpen .rdtPicker { + color: black; + } + + .editor .editor-sidebar .components-container .component-image-holder { + background: #2f3c4c!important; + border: 1px solid #2f3c4c!important; + + center, .component-title { + filter: brightness(0) invert(1); + } + } + + .nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { + border-color: transparent!important; +} + + .modal-content, .modal-header { + background-color: #1f2936!important; + .text-muted { + color: #c3c3c3!important; + } + } + + .modal-header { + border-bottom: 1px solid rgba(255, 255, 255, 0.09)!important; + } + + .canvas-container { + background-color: #1f2936; + } + + .editor .main .query-pane { + border: solid rgba(255, 255, 255, 0.09)!important; + border-width: 1px 0px 0px 0px!important; + } + + .no-components-box { + background-color: #1f2936!important; + + center { + filter: brightness(0) invert(1); + } + } + + .query-list { + .text-muted { + color: #c3c3c3!important; + } + } + + .left-sidebar, .editor-sidebar { + background-color: #1f2936!important; + } + + .editor-sidebar { + border: solid rgba(255, 255, 255, 0.09); + border-width: 0px 0px 0px 1px!important; + + .nav-tabs { + border-bottom: 1px solid rgba(255, 255, 255, 0.09)!important; + } + } + + .editor .editor-sidebar .nav-tabs .nav-link { + color: #fff; + img { + filter: brightness(0) invert(1); + } + } + + .jet-table { + background-color: #1f2936!important; + } + + .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { + background-color: #2f3c4c; + border-color: transparent!important; + } + + .editor .main .query-pane .query-definition-pane .header { + border: solid rgba(255, 255, 255, 0.09); + border-width: 0px 0px 1px 0px!important; + background: #1f2936; + } + + .left-sidebar { + border: solid rgba(255, 255, 255, 0.09); + border-width: 0px 1px 3px 0px; + .text-muted { + color: #c3c3c3!important; + } + } + + .folder-list { + color: #c3c3c3!important; + } + + .app-title { + color: #c3c3c3!important; + } + + .RichEditor-root { + background: #1f2936; + border: 1px solid #2f3c4c; + } + + .app-description { + color: #c3c3c3!important; + } + + .btn-light, .btn-outline-light { + background-color: #42546a!important; + + img { + filter: brightness(0) invert(1); + } + } + + .editor .left-sidebar .datasources-container tr { + border-bottom: solid 1px rgba(255, 255, 255, 0.09); + } + + .editor .left-sidebar .datasources-container .datasources-header { + border: solid rgba(255, 255, 255, 0.09)!important; + border-width: 0px 0px 1px 0px!important; +} + + .query-manager-header .nav-item { + border-right: solid 1px rgba(255, 255, 255, 0.09); + .nav-link { + color: #c3c3c3; + } + } + + .input-group-text { + border: solid 1px rgba(255, 255, 255, 0.09)!important; + } + + .app-users-list { + .text-muted { + color: #c3c3c3!important; + } + } + + .data-pane { + border: solid rgba(255, 255, 255, 0.09)!important; + border-width: 0px 1px 0px 0px!important; + } + + .main .query-pane .data-pane .queries-container .queries-header { + border: solid rgba(255, 255, 255, 0.09)!important; + border-width: 0px 0px 1px 0px!important; + + .text-muted { + color: #c3c3c3!important; + } + } + + .query-pane { + background-color: #1f2936!important; + } + + .svg-icon { + filter: brightness(0) invert(1); + } + + .badge { + filter: brightness(1) invert(1); + + .svg-icon { + filter: brightness(1) invert(0); + } + } + + .alert { + background: transparent; + .text-muted { + color: #c3c3c3!important; + } + } + + .editor .editor-sidebar .inspector .header { + border: solid rgba(255, 255, 255, 0.09)!important; + border-width: 0px 0px 1px 0px!important; + } + + .skeleton-line::after { + // background-image: linear-gradient(to right, #232e3c 0, #4c5b79 40%, #4c5b79 80%); + background-image: linear-gradient(to right, #566177 0, #5a6170 40%, #4c5b79 80%); + } + + // .btn { + // filter: brightness(1) invert(1); + // } + + .select-search__input { + color: rgb(224, 224, 224); + background-color: #2b3547; + border: 1px solid #2b3547; + } + + .select-search__select { + background: #fff; + box-shadow: 0 0.0625rem 0.125rem rgba(0, 0, 0, 0.15); + } + + .select-search__row:not(:first-child) { + border-top: 1px solid #eee; + } + + .select-search__option, + .select-search__not-found { + background: #fff; + } + + .select-search__option.is-highlighted, + .select-search__option:not(.is-selected):hover { + background: rgba(47, 204, 139, 0.1); + } + + .select-search__option.is-highlighted.is-selected, + .select-search__option.is-selected:hover { + background: #2eb378; + color: #fff; + } + + .org-users-page { + .user-email, .user-status { + filter: brightness(0) invert(1); + } + } + + .react-json-view { + background-color: transparent!important; + } + + .codehinter-default-input { + background-color: transparent; + border: 1px solid #333c48; + } + + .color-picker-input { + border: solid 1px #333c48; + } + + .codehinter-query-editor-input { + background-color: #272822; + border: 1px solid #2c3a4c; + } + + .codehinter-query-editor-input .CodeMirror { + height: 31px !important; + } + + .codehinter-query-editor-input .CodeMirror { + color: #c3c3c3!important; + } + + .select-search:not(.is-loading):not(.select-search--multiple) .select-search__value::after { + transform: rotate( + 45deg + ); + border-right: 1px solid #fff; + border-bottom: 1px solid #fff; + } + } + +.main-wrapper { + position: fixed; + min-height: 100%; + min-width: 100%; +} + +.jet-table { + .global-search-field { + background: transparent; + } +} + +.modal-backdrop.show { + opacity: 0.74; +} \ No newline at end of file diff --git a/lib/json_web_token.rb b/lib/json_web_token.rb index ffcfc5627b..3270e94ebc 100644 --- a/lib/json_web_token.rb +++ b/lib/json_web_token.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + class JsonWebToken class << self def encode(payload, exp = 30.days.from_now) payload[:exp] = exp.to_i - JWT.encode(payload, ENV.fetch('SECRET_KEY_BASE')) + JWT.encode(payload, ENV.fetch("SECRET_KEY_BASE")) end def decode(token) - body = JWT.decode(token, ENV.fetch('SECRET_KEY_BASE'))[0] + body = JWT.decode(token, ENV.fetch("SECRET_KEY_BASE"))[0] HashWithIndifferentAccess.new body rescue StandardError nil diff --git a/test/channels/application_cable/connection_test.rb b/test/channels/application_cable/connection_test.rb index d05dbd24cf..cc8337fc6d 100644 --- a/test/channels/application_cable/connection_test.rb +++ b/test/channels/application_cable/connection_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase # test "connects with cookies" do diff --git a/test/controllers/app_users_controller_test.rb b/test/controllers/app_users_controller_test.rb index 55734646e6..5901cc04c4 100644 --- a/test/controllers/app_users_controller_test.rb +++ b/test/controllers/app_users_controller_test.rb @@ -1,69 +1,71 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class AppUsersControllerTest < ActionDispatch::IntegrationTest def setup - @org = Organization.create({ name: 'ToolJet Test' }) - @org_admin = User.create({ first_name: 'Admin', email: 'admin@example.com', password: 'password', + @org = Organization.create({ name: "ToolJet Test" }) + @org_admin = User.create({ first_name: "Admin", email: "admin@example.com", password: "password", organization: @org }) - @org_developer = User.create({ first_name: 'Dev', email: 'dev@example.com', password: 'password', + @org_developer = User.create({ first_name: "Dev", email: "dev@example.com", password: "password", organization: @org }) - @org_viewer = User.create({ first_name: 'Viewer', email: 'viewer@example.com', password: 'password', + @org_viewer = User.create({ first_name: "Viewer", email: "viewer@example.com", password: "password", organization: @org }) - @org_user_admin = OrganizationUser.create(organization: @org, user: @org_admin, role: 'admin', status: 'active') - @org_user_developer = OrganizationUser.create(organization: @org, user: @org_developer, role: 'developer') - @org_user_viewer = OrganizationUser.create(organization: @org, user: @org_viewer, role: 'viewer', status: 'active') + @org_user_admin = OrganizationUser.create(organization: @org, user: @org_admin, role: "admin", status: "active") + @org_user_developer = OrganizationUser.create(organization: @org, user: @org_developer, role: "developer") + @org_user_viewer = OrganizationUser.create(organization: @org, user: @org_viewer, role: "viewer", status: "active") - @another_org = Organization.create({ name: 'Another ToolJet Test' }) - @another_org_admin = User.create({ first_name: 'Admin', email: 'admin@domain.com', password: 'password', + @another_org = Organization.create({ name: "Another ToolJet Test" }) + @another_org_admin = User.create({ first_name: "Admin", email: "admin@domain.com", password: "password", organization: @another_org }) @org_another_org_admin = OrganizationUser.create(organization: @another_org, user: @another_org_admin, - role: 'admin') + role: "admin") @first_app = App.create({ - name: 'Test App', + name: "Test App", organization: @org_admin.organization }) end - test 'org admins can create app users' do - assert_difference 'AppUser.count', 1 do - post '/app_users/', params: { + test "org admins can create app users" do + assert_difference "AppUser.count", 1 do + post "/app_users/", params: { org_user_id: @org_user_admin.id, app_id: @first_app.id, - role: 'admin' + role: "admin" }, as: :json, headers: auth_header(@org_admin) end end - test 'non org-admins cannot create app users' do - assert_no_difference 'AppUser.count' do - post '/app_users/', params: { + test "non org-admins cannot create app users" do + assert_no_difference "AppUser.count" do + post "/app_users/", params: { org_user_id: @org_user_developer.id, app_id: @first_app.id, - role: 'admin' + role: "admin" }, as: :json, headers: auth_header(@org_developer) end end - test 'app admins can create app users' do - AppUser.create(user: @org_viewer, app: @first_app, role: 'admin') + test "app admins can create app users" do + AppUser.create(user: @org_viewer, app: @first_app, role: "admin") - assert_difference 'AppUser.count', 1 do - post '/app_users/', params: { + assert_difference "AppUser.count", 1 do + post "/app_users/", params: { org_user_id: @org_user_developer.id, app_id: @first_app.id, - role: 'admin' + role: "admin" }, as: :json, headers: auth_header(@org_viewer) end end - test 'admin of another org cannot create app users' do - assert_no_difference 'AppUser.count' do - post '/app_users/', params: { + test "admin of another org cannot create app users" do + assert_no_difference "AppUser.count" do + post "/app_users/", params: { org_user_id: @org_user_admin.id, app_id: @first_app.id, - role: 'admin' + role: "admin" }, as: :json, headers: auth_header(@another_org_admin) end end diff --git a/test/controllers/application_controller_test.rb b/test/controllers/application_controller_test.rb index b7bfb8d4cb..49d51cd92e 100644 --- a/test/controllers/application_controller_test.rb +++ b/test/controllers/application_controller_test.rb @@ -1,28 +1,28 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class ApplicationControllerTest < ActionDispatch::IntegrationTest - def setup - @org = Organization.create({ name: 'ToolJet Test' }) - @org_admin = User.create({ first_name: 'Admin', email: 'admin@example.com', password: 'password', + @org = Organization.create({ name: "ToolJet Test" }) + @org_admin = User.create({ first_name: "Admin", email: "admin@example.com", password: "password", organization: @org }) - @admin_org_user = OrganizationUser.create(organization: @org, user: @org_admin, role: 'admin', status: 'active') + @admin_org_user = OrganizationUser.create(organization: @org, user: @org_admin, role: "admin", status: "active") end test "active users can access authenticated routes" do - app = App.create(name: 'Test App', organization: @org) - get apps_url, headers: { 'Content-Type': 'application/json' }.merge(auth_header(@org_admin)), xhr: true + app = App.create(name: "Test App", organization: @org) + get apps_url, headers: { "Content-Type": "application/json" }.merge(auth_header(@org_admin)), xhr: true assert_response 200 end test "archived users cannot access authenticated routes" do - @admin_org_user.update(status: 'archived') + @admin_org_user.update(status: "archived") @admin_org_user.reload - app = App.create(name: 'Test App', organization: @org) - get apps_url, headers: { 'Content-Type': 'application/json' }.merge(auth_header(@org_admin)), xhr: true + app = App.create(name: "Test App", organization: @org) + get apps_url, headers: { "Content-Type": "application/json" }.merge(auth_header(@org_admin)), xhr: true assert_response 401 end - end diff --git a/test/controllers/apps_controller_test.rb b/test/controllers/apps_controller_test.rb index 98af6cbfd2..94fb10fdf0 100644 --- a/test/controllers/apps_controller_test.rb +++ b/test/controllers/apps_controller_test.rb @@ -1,100 +1,102 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class AppsControllerTest < ActionDispatch::IntegrationTest def setup - @org = Organization.create({ name: 'ToolJet Test' }) - @admin = User.create({ first_name: 'Admin', email: 'admin@example.com', password: 'password', + @org = Organization.create({ name: "ToolJet Test" }) + @admin = User.create({ first_name: "Admin", email: "admin@example.com", password: "password", organization: @org }) - @developer = User.create({ first_name: 'Dev', email: 'dev@example.com', password: 'password', + @developer = User.create({ first_name: "Dev", email: "dev@example.com", password: "password", organization: @org }) - @viewer = User.create({ first_name: 'Viewer', email: 'viewer@example.com', password: 'password', + @viewer = User.create({ first_name: "Viewer", email: "viewer@example.com", password: "password", organization: @org }) - OrganizationUser.create(organization: @org, user: @admin, role: 'admin', status: 'active') - OrganizationUser.create(organization: @org, user: @developer, role: 'developer', status: 'active') - OrganizationUser.create(organization: @org, user: @viewer, role: 'viewer', status: 'active') + OrganizationUser.create(organization: @org, user: @admin, role: "admin", status: "active") + OrganizationUser.create(organization: @org, user: @developer, role: "developer", status: "active") + OrganizationUser.create(organization: @org, user: @viewer, role: "viewer", status: "active") - @another_org = Organization.create({ name: 'Another ToolJet Test' }) - @another_org_admin = User.create({ first_name: 'Admin', email: 'admin@domain.com', password: 'password', + @another_org = Organization.create({ name: "Another ToolJet Test" }) + @another_org_admin = User.create({ first_name: "Admin", email: "admin@domain.com", password: "password", organization: @another_org }) - OrganizationUser.create(organization: @another_org, user: @another_org_admin, role: 'admin', status: 'active') + OrganizationUser.create(organization: @another_org, user: @another_org_admin, role: "admin", status: "active") end - test 'admins can create apps' do - assert_difference 'App.count', 1 do - post apps_url, params: { name: 'Test App' }, as: :json, headers: auth_header(@admin) + test "admins can create apps" do + assert_difference "App.count", 1 do + post apps_url, params: { name: "Test App" }, as: :json, headers: auth_header(@admin) end assert_response 200 - get apps_url, headers: { 'Content-Type': 'application/json' }.merge(auth_header(@admin)), xhr: true + get apps_url, headers: { "Content-Type": "application/json" }.merge(auth_header(@admin)), xhr: true assert_response 200 - assert_equal 1, JSON.parse(response.body)['apps'].count + assert_equal 1, JSON.parse(response.body)["apps"].count end - test 'developers cannot create apps' do - assert_no_difference 'App.count' do - post apps_url, params: { name: 'Test App' }, as: :json, headers: auth_header(@developer) + test "developers cannot create apps" do + assert_no_difference "App.count" do + post apps_url, params: { name: "Test App" }, as: :json, headers: auth_header(@developer) end assert_response 403 end - test 'viewers cannot create apps' do - assert_no_difference 'App.count' do - post apps_url, params: { name: 'Test App' }, as: :json, headers: auth_header(@viewer) + test "viewers cannot create apps" do + assert_no_difference "App.count" do + post apps_url, params: { name: "Test App" }, as: :json, headers: auth_header(@viewer) end assert_response 403 end - test 'all org users can GET app' do - app = App.create(name: 'Test App', organization: @org) + test "all org users can GET app" do + app = App.create(name: "Test App", organization: @org) - get app_url(app.id), headers: { 'Content-Type': 'application/json' }.merge(auth_header(@admin)), xhr: true + get app_url(app.id), headers: { "Content-Type": "application/json" }.merge(auth_header(@admin)), xhr: true assert_response 200 - get app_url(app.id), headers: { 'Content-Type': 'application/json' }.merge(auth_header(@developer)), xhr: true + get app_url(app.id), headers: { "Content-Type": "application/json" }.merge(auth_header(@developer)), xhr: true assert_response 200 - get app_url(app.id), headers: { 'Content-Type': 'application/json' }.merge(auth_header(@viewer)), xhr: true + get app_url(app.id), headers: { "Content-Type": "application/json" }.merge(auth_header(@viewer)), xhr: true assert_response 200 end - test 'non org users cannot GET app' do - app = App.create(name: 'Test App', organization: @org) + test "non org users cannot GET app" do + app = App.create(name: "Test App", organization: @org) - get app_url(app.id), headers: { 'Content-Type': 'application/json' }.merge(auth_header(@another_org_admin)), + get app_url(app.id), headers: { "Content-Type": "application/json" }.merge(auth_header(@another_org_admin)), xhr: true assert_response 403 end - test 'users cannot list another orgs apps' do - app = App.create(name: 'Test App', organization: @org) + test "users cannot list another orgs apps" do + app = App.create(name: "Test App", organization: @org) - get apps_url, headers: { 'Content-Type': 'application/json' }.merge(auth_header(@another_org_admin)), xhr: true + get apps_url, headers: { "Content-Type": "application/json" }.merge(auth_header(@another_org_admin)), xhr: true assert_response 200 - assert_equal 0, JSON.parse(response.body)['apps'].size + assert_equal 0, JSON.parse(response.body)["apps"].size end - test 'org users can list orgs apps' do - app = App.create(name: 'Test App', organization: @org) + test "org users can list orgs apps" do + app = App.create(name: "Test App", organization: @org) - get apps_url, headers: { 'Content-Type': 'application/json' }.merge(auth_header(@admin)), xhr: true + get apps_url, headers: { "Content-Type": "application/json" }.merge(auth_header(@admin)), xhr: true assert_response 200 - assert_equal 1, JSON.parse(response.body)['apps'].size + assert_equal 1, JSON.parse(response.body)["apps"].size - get apps_url, headers: { 'Content-Type': 'application/json' }.merge(auth_header(@developer)), xhr: true + get apps_url, headers: { "Content-Type": "application/json" }.merge(auth_header(@developer)), xhr: true assert_response 200 - assert_equal 1, JSON.parse(response.body)['apps'].size + assert_equal 1, JSON.parse(response.body)["apps"].size - get apps_url, headers: { 'Content-Type': 'application/json' }.merge(auth_header(@viewer)), xhr: true + get apps_url, headers: { "Content-Type": "application/json" }.merge(auth_header(@viewer)), xhr: true assert_response 200 - assert_equal 1, JSON.parse(response.body)['apps'].size + assert_equal 1, JSON.parse(response.body)["apps"].size end - test 'anyone can view public apps' do - app = App.create(name: 'Test App', organization: @org, is_public: true) - get app_url(app.id), headers: { 'Content-Type': 'application/json' }, + test "anyone can view public apps" do + app = App.create(name: "Test App", organization: @org, is_public: true) + get app_url(app.id), headers: { "Content-Type": "application/json" }, xhr: true assert_response 200 end diff --git a/test/controllers/authentication_controller_test.rb b/test/controllers/authentication_controller_test.rb index d8285dd4bf..1d7882b581 100644 --- a/test/controllers/authentication_controller_test.rb +++ b/test/controllers/authentication_controller_test.rb @@ -1,18 +1,19 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class AuthenticationControllerTest < ActionDispatch::IntegrationTest - def setup - @org = Organization.create({ name: 'ToolJet Test' }) - @org_admin = User.create({ first_name: 'Admin', email: 'admin@example.com', password: 'password', + @org = Organization.create({ name: "ToolJet Test" }) + @org_admin = User.create({ first_name: "Admin", email: "admin@example.com", password: "password", organization: @org }) - @admin_org_user = OrganizationUser.create(organization: @org, user: @org_admin, role: 'admin', status: 'active') + @admin_org_user = OrganizationUser.create(organization: @org, user: @org_admin, role: "admin", status: "active") end test "can login if org user is active" do - post '/authenticate/', params: { - email: 'admin@example.com', - password: 'password', + post "/authenticate/", params: { + email: "admin@example.com", + password: "password", }, as: :json assert_equal "200", response.code @@ -20,15 +21,14 @@ class AuthenticationControllerTest < ActionDispatch::IntegrationTest test "cannot login if org user is not active" do - @admin_org_user.update(status: 'archived') + @admin_org_user.update(status: "archived") @admin_org_user.reload - post '/authenticate/', params: { - email: 'admin@example.com', - password: 'password', + post "/authenticate/", params: { + email: "admin@example.com", + password: "password", }, as: :json assert_equal "401", response.code end - end diff --git a/test/controllers/data_queries_controller_test.rb b/test/controllers/data_queries_controller_test.rb index 41ee6d6bde..fc63a7e7da 100644 --- a/test/controllers/data_queries_controller_test.rb +++ b/test/controllers/data_queries_controller_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class DataQueriesControllerTest < ActionDispatch::IntegrationTest # test "the truth" do diff --git a/test/controllers/data_sources_controller_test.rb b/test/controllers/data_sources_controller_test.rb index 252b2818c4..b5d38eda66 100644 --- a/test/controllers/data_sources_controller_test.rb +++ b/test/controllers/data_sources_controller_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class DataSourcesControllerTest < ActionDispatch::IntegrationTest # test "the truth" do diff --git a/test/controllers/folder_apps_controller_test.rb b/test/controllers/folder_apps_controller_test.rb index 461286f134..2feeeb80d4 100644 --- a/test/controllers/folder_apps_controller_test.rb +++ b/test/controllers/folder_apps_controller_test.rb @@ -1,48 +1,50 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class FolderAppsControllerTest < ActionDispatch::IntegrationTest def setup - @org = Organization.create({ name: 'ToolJet Test' }) - @admin = User.create({ first_name: 'Admin', email: 'admin@example.com', password: 'password', + @org = Organization.create({ name: "ToolJet Test" }) + @admin = User.create({ first_name: "Admin", email: "admin@example.com", password: "password", organization: @org }) - @developer = User.create({ first_name: 'Dev', email: 'dev@example.com', password: 'password', + @developer = User.create({ first_name: "Dev", email: "dev@example.com", password: "password", organization: @org }) - @viewer = User.create({ first_name: 'Viewer', email: 'viewer@example.com', password: 'password', + @viewer = User.create({ first_name: "Viewer", email: "viewer@example.com", password: "password", organization: @org }) - OrganizationUser.create(organization: @org, user: @admin, role: 'admin', status: 'active') - OrganizationUser.create(organization: @org, user: @developer, role: 'developer', status: 'active') - OrganizationUser.create(organization: @org, user: @viewer, role: 'viewer', status: 'active') + OrganizationUser.create(organization: @org, user: @admin, role: "admin", status: "active") + OrganizationUser.create(organization: @org, user: @developer, role: "developer", status: "active") + OrganizationUser.create(organization: @org, user: @viewer, role: "viewer", status: "active") - @another_org = Organization.create({ name: 'Another ToolJet Test' }) - @another_org_admin = User.create({ first_name: 'Admin', email: 'admin@domain.com', password: 'password', + @another_org = Organization.create({ name: "Another ToolJet Test" }) + @another_org_admin = User.create({ first_name: "Admin", email: "admin@domain.com", password: "password", organization: @another_org }) - OrganizationUser.create(organization: @another_org, user: @another_org_admin, role: 'admin') + OrganizationUser.create(organization: @another_org, user: @another_org_admin, role: "admin") - @folder = Folder.create(name: 'Test Folder', organization: @org) + @folder = Folder.create(name: "Test Folder", organization: @org) end - test "org admins can add apps to folders" do - app = App.create(name: 'Test App', organization: @org) - assert_difference 'FolderApp.count', 1 do - get '/folder_apps', params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@admin) + test "org admins can add apps to folders" do + app = App.create(name: "Test App", organization: @org) + assert_difference "FolderApp.count", 1 do + get "/folder_apps", params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@admin) end end - test "admins of a different org cannot add apps to folders" do - app = App.create(name: 'Test App', organization: @org) - assert_difference 'FolderApp.count', 0 do - get '/folder_apps', params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@another_org_admin) + test "admins of a different org cannot add apps to folders" do + app = App.create(name: "Test App", organization: @org) + assert_difference "FolderApp.count", 0 do + get "/folder_apps", params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@another_org_admin) end end - test "only admins & developers can add apps to folders" do - app = App.create(name: 'Test App', organization: @org) - assert_difference 'FolderApp.count', 1 do - get '/folder_apps', params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@developer) + test "only admins & developers can add apps to folders" do + app = App.create(name: "Test App", organization: @org) + assert_difference "FolderApp.count", 1 do + get "/folder_apps", params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@developer) end - assert_difference 'FolderApp.count', 0 do - get '/folder_apps', params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@viewer) + assert_difference "FolderApp.count", 0 do + get "/folder_apps", params: { app_id: app.id, folder_id: @folder.id }, as: :json, headers: auth_header(@viewer) end end end diff --git a/test/controllers/folders_controller_test.rb b/test/controllers/folders_controller_test.rb index 45e146cf8e..f4d4b657ec 100644 --- a/test/controllers/folders_controller_test.rb +++ b/test/controllers/folders_controller_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "test_helper" class FoldersControllerTest < ActionDispatch::IntegrationTest diff --git a/test/controllers/forgot_password_controller_test.rb b/test/controllers/forgot_password_controller_test.rb index 5c67e21620..f489318e73 100644 --- a/test/controllers/forgot_password_controller_test.rb +++ b/test/controllers/forgot_password_controller_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "test_helper" class ForgotPasswordControllerTest < ActionDispatch::IntegrationTest diff --git a/test/controllers/metadata_controller_test.rb b/test/controllers/metadata_controller_test.rb index 29bbf3a11f..8ffbd79f81 100644 --- a/test/controllers/metadata_controller_test.rb +++ b/test/controllers/metadata_controller_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "test_helper" class MetadataControllerTest < ActionDispatch::IntegrationTest diff --git a/test/controllers/organization_users_controller_test.rb b/test/controllers/organization_users_controller_test.rb index 503814c73c..da45d84ad2 100644 --- a/test/controllers/organization_users_controller_test.rb +++ b/test/controllers/organization_users_controller_test.rb @@ -1,127 +1,129 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class OrganizationUsersControllerTest < ActionDispatch::IntegrationTest def setup - @org = Organization.create({ name: 'ToolJet Test' }) - @admin = User.create({ first_name: 'Admin', email: 'admin@example.com', password: 'password', + @org = Organization.create({ name: "ToolJet Test" }) + @admin = User.create({ first_name: "Admin", email: "admin@example.com", password: "password", organization: @org }) - @developer = User.create({ first_name: 'Dev', email: 'dev@example.com', password: 'password', + @developer = User.create({ first_name: "Dev", email: "dev@example.com", password: "password", organization: @org }) - @viewer = User.create({ first_name: 'Viewer', email: 'viewer@example.com', password: 'password', + @viewer = User.create({ first_name: "Viewer", email: "viewer@example.com", password: "password", organization: @org }) - @org_user_admin = OrganizationUser.create(organization: @org, user: @admin, role: 'admin', status: 'active') - @org_user_developer = OrganizationUser.create(organization: @org, user: @developer, role: 'developer', status: 'active') - @org_user_viewer = OrganizationUser.create(organization: @org, user: @viewer, role: 'viewer', status: 'active') + @org_user_admin = OrganizationUser.create(organization: @org, user: @admin, role: "admin", status: "active") + @org_user_developer = OrganizationUser.create(organization: @org, user: @developer, role: "developer", status: "active") + @org_user_viewer = OrganizationUser.create(organization: @org, user: @viewer, role: "viewer", status: "active") - @another_org = Organization.create({ name: 'Another ToolJet Test' }) - @another_org_admin = User.create({ first_name: 'Admin', email: 'admin@domain.com', password: 'password', + @another_org = Organization.create({ name: "Another ToolJet Test" }) + @another_org_admin = User.create({ first_name: "Admin", email: "admin@domain.com", password: "password", organization: @another_org }) @org_another_org_admin = OrganizationUser.create(organization: @another_org, user: @another_org_admin, - role: 'admin', status: 'active') + role: "admin", status: "active") end # POST /create tests - test 'org admins can create org users' do - assert_difference 'OrganizationUser.count', 1 do - post '/organization_users', params: org_user_params, as: :json, headers: auth_header(@admin) + test "org admins can create org users" do + assert_difference "OrganizationUser.count", 1 do + post "/organization_users", params: org_user_params, as: :json, headers: auth_header(@admin) end end - test 'org admins cannot create org users if email already exists' do - post '/organization_users', params: org_user_params, as: :json, headers: auth_header(@admin) - post '/organization_users', params: org_user_params, as: :json, headers: auth_header(@admin) + test "org admins cannot create org users if email already exists" do + post "/organization_users", params: org_user_params, as: :json, headers: auth_header(@admin) + post "/organization_users", params: org_user_params, as: :json, headers: auth_header(@admin) assert_response 422 - assert_equal "Email address is already taken", JSON.parse(response.body)['message'] + assert_equal "Email address is already taken", JSON.parse(response.body)["message"] end - test 'OrganizationUser should be unique per organization and user' do + test "OrganizationUser should be unique per organization and user" do assert_raises(ActiveRecord::RecordNotUnique) do - org_user = OrganizationUser.new(organization: @org, user: @admin, role: 'admin', status: 'active') + org_user = OrganizationUser.new(organization: @org, user: @admin, role: "admin", status: "active") org_user.save end end - test 'cannot create org users if not admin' do - assert_no_difference 'OrganizationUser.count' do - post '/organization_users', params: org_user_params, as: :json, headers: auth_header(@developer) + test "cannot create org users if not admin" do + assert_no_difference "OrganizationUser.count" do + post "/organization_users", params: org_user_params, as: :json, headers: auth_header(@developer) end - assert_no_difference 'OrganizationUser.count' do - post '/organization_users', params: org_user_params, as: :json, headers: auth_header(@viewer) + assert_no_difference "OrganizationUser.count" do + post "/organization_users", params: org_user_params, as: :json, headers: auth_header(@viewer) end end # POST /change_role tests - test 'org admins can change role of org users' do - assert_equal 'developer', @org_user_developer.role - post organization_user_change_role_url(@org_user_developer.id), params: { role: 'viewer' }, as: :json, + test "org admins can change role of org users" do + assert_equal "developer", @org_user_developer.role + post organization_user_change_role_url(@org_user_developer.id), params: { role: "viewer" }, as: :json, headers: auth_header(@admin) assert_response 204 - assert_equal 'viewer', @org_user_developer.reload.role + assert_equal "viewer", @org_user_developer.reload.role end - test 'cannot change role of org users if not org admin' do - assert_equal 'developer', @org_user_developer.role - post organization_user_change_role_url(@org_user_developer.id), params: { role: 'viewer' }, as: :json, + test "cannot change role of org users if not org admin" do + assert_equal "developer", @org_user_developer.role + post organization_user_change_role_url(@org_user_developer.id), params: { role: "viewer" }, as: :json, headers: auth_header(@viewer) assert_response 403 - assert_equal 'developer', @org_user_developer.reload.role + assert_equal "developer", @org_user_developer.reload.role - assert_equal 'viewer', @org_user_viewer.role - post organization_user_change_role_url(@org_user_viewer.id), params: { role: 'viewer' }, as: :json, + assert_equal "viewer", @org_user_viewer.role + post organization_user_change_role_url(@org_user_viewer.id), params: { role: "viewer" }, as: :json, headers: auth_header(@developer) assert_response 403 - assert_equal 'viewer', @org_user_viewer.reload.role + assert_equal "viewer", @org_user_viewer.reload.role end - test 'org users of one org cannot change role of users of another org' do - assert_equal 'admin', @org_another_org_admin.role - post organization_user_change_role_url(@org_another_org_admin.id), params: { role: 'viewer' }, as: :json, + test "org users of one org cannot change role of users of another org" do + assert_equal "admin", @org_another_org_admin.role + post organization_user_change_role_url(@org_another_org_admin.id), params: { role: "viewer" }, as: :json, headers: auth_header(@admin) assert_response 403 - assert_equal 'admin', @org_another_org_admin.reload.role + assert_equal "admin", @org_another_org_admin.reload.role end ## POST /archive tests - test 'org admins can archive org users' do - assert_equal 'active', @org_user_developer.status + test "org admins can archive org users" do + assert_equal "active", @org_user_developer.status post organization_user_archive_url(@org_user_developer.id), as: :json, headers: auth_header(@admin) assert_response 204 - assert_equal 'archived', @org_user_developer.reload.status + assert_equal "archived", @org_user_developer.reload.status end - test 'cannot archive user if not org admin' do - assert_equal 'active', @org_user_developer.status + test "cannot archive user if not org admin" do + assert_equal "active", @org_user_developer.status post organization_user_archive_url(@org_user_developer.id), as: :json, headers: auth_header(@viewer) assert_response 403 - assert_equal 'active', @org_user_developer.reload.status + assert_equal "active", @org_user_developer.reload.status - assert_equal 'active', @org_user_viewer.status + assert_equal "active", @org_user_viewer.status post organization_user_archive_url(@org_user_viewer.id), as: :json, headers: auth_header(@developer) assert_response 403 - assert_equal 'active', @org_user_viewer.reload.status + assert_equal "active", @org_user_viewer.reload.status end - test 'cannot archive user of another org' do - assert_equal 'active', @org_another_org_admin.status + test "cannot archive user of another org" do + assert_equal "active", @org_another_org_admin.status post organization_user_change_role_url(@org_another_org_admin.id), as: :json, headers: auth_header(@admin) assert_response 403 - assert_equal 'active', @org_another_org_admin.reload.status + assert_equal "active", @org_another_org_admin.reload.status end private - def org_user_params - { - first_name: 'test', - last_name: 'user', - email: 'user@example.com', - role: 'admin' - } - end + def org_user_params + { + first_name: "test", + last_name: "user", + email: "user@example.com", + role: "admin" + } + end end diff --git a/test/controllers/organizations_controller_test.rb b/test/controllers/organizations_controller_test.rb index e0c4e350fe..816a6f958d 100644 --- a/test/controllers/organizations_controller_test.rb +++ b/test/controllers/organizations_controller_test.rb @@ -1,49 +1,51 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class OrganizationsControllerTest < ActionDispatch::IntegrationTest def setup - @org = Organization.create({ name: 'ToolJet Test' }) - @admin = User.create({ first_name: 'Admin', email: 'admin@example.com', password: 'password', + @org = Organization.create({ name: "ToolJet Test" }) + @admin = User.create({ first_name: "Admin", email: "admin@example.com", password: "password", organization: @org }) - @developer = User.create({ first_name: 'Dev', email: 'dev@example.com', password: 'password', + @developer = User.create({ first_name: "Dev", email: "dev@example.com", password: "password", organization: @org }) - @viewer = User.create({ first_name: 'Viewer', email: 'viewer@example.com', password: 'password', + @viewer = User.create({ first_name: "Viewer", email: "viewer@example.com", password: "password", organization: @org }) - OrganizationUser.create(organization: @org, user: @admin, role: 'admin', status: 'active') - OrganizationUser.create(organization: @org, user: @developer, role: 'developer', status: 'active') - OrganizationUser.create(organization: @org, user: @viewer, role: 'viewer', status: 'active') + OrganizationUser.create(organization: @org, user: @admin, role: "admin", status: "active") + OrganizationUser.create(organization: @org, user: @developer, role: "developer", status: "active") + OrganizationUser.create(organization: @org, user: @viewer, role: "viewer", status: "active") - @another_org = Organization.create({ name: 'Another ToolJet Test' }) - @another_org_admin = User.create({ first_name: 'Admin', email: 'admin@domain.com', password: 'password', + @another_org = Organization.create({ name: "Another ToolJet Test" }) + @another_org_admin = User.create({ first_name: "Admin", email: "admin@domain.com", password: "password", organization: @another_org }) - OrganizationUser.create(organization: @another_org, user: @another_org_admin, role: 'admin', status: 'active') + OrganizationUser.create(organization: @another_org, user: @another_org_admin, role: "admin", status: "active") end - test 'org users can list users of the org' do - get organization_users_url(@org.id), headers: { 'Content-Type': 'application/json' }.merge(auth_header(@admin)), + test "org users can list users of the org" do + get organization_users_url(@org.id), headers: { "Content-Type": "application/json" }.merge(auth_header(@admin)), xhr: true assert_response 200 - assert_equal 3, JSON.parse(response.body)['users'].size + assert_equal 3, JSON.parse(response.body)["users"].size get organization_users_url(@org.id), - headers: { 'Content-Type': 'application/json' }.merge(auth_header(@developer)), xhr: true + headers: { "Content-Type": "application/json" }.merge(auth_header(@developer)), xhr: true assert_response 200 - assert_equal 3, JSON.parse(response.body)['users'].size + assert_equal 3, JSON.parse(response.body)["users"].size - get organization_users_url(@org.id), headers: { 'Content-Type': 'application/json' }.merge(auth_header(@viewer)), + get organization_users_url(@org.id), headers: { "Content-Type": "application/json" }.merge(auth_header(@viewer)), xhr: true assert_response 200 - assert_equal 3, JSON.parse(response.body)['users'].size + assert_equal 3, JSON.parse(response.body)["users"].size get organization_users_url(@another_org.id), - headers: { 'Content-Type': 'application/json' }.merge(auth_header(@another_org_admin)), xhr: true + headers: { "Content-Type": "application/json" }.merge(auth_header(@another_org_admin)), xhr: true assert_response 200 - assert_equal 1, JSON.parse(response.body)['users'].size + assert_equal 1, JSON.parse(response.body)["users"].size # Even if org id is given, current user's org's users are returned get organization_users_url(@org.id), - headers: { 'Content-Type': 'application/json' }.merge(auth_header(@another_org_admin)), xhr: true + headers: { "Content-Type": "application/json" }.merge(auth_header(@another_org_admin)), xhr: true assert_response 200 - assert_equal 1, JSON.parse(response.body)['users'].size + assert_equal 1, JSON.parse(response.body)["users"].size end end diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 6c3da770c9..c8182e82b7 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class UsersControllerTest < ActionDispatch::IntegrationTest # test "the truth" do diff --git a/test/controllers/versions_controller_test.rb b/test/controllers/versions_controller_test.rb index 87a36a2e9f..de9f489a79 100644 --- a/test/controllers/versions_controller_test.rb +++ b/test/controllers/versions_controller_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class VersionsControllerTest < ActionDispatch::IntegrationTest # test "the truth" do diff --git a/test/mailers/previews/user_mailer_preview.rb b/test/mailers/previews/user_mailer_preview.rb index 38267b036e..b069450344 100644 --- a/test/mailers/previews/user_mailer_preview.rb +++ b/test/mailers/previews/user_mailer_preview.rb @@ -1,3 +1,6 @@ +# frozen_string_literal: true + # Preview all emails at http://localhost:3000/rails/mailers/user_mailer + class UserMailerPreview < ActionMailer::Preview end diff --git a/test/mailers/user_mailer_test.rb b/test/mailers/user_mailer_test.rb index 67a1629cc9..15634f2d60 100644 --- a/test/mailers/user_mailer_test.rb +++ b/test/mailers/user_mailer_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class UserMailerTest < ActionMailer::TestCase # test "the truth" do diff --git a/test/models/app_user_test.rb b/test/models/app_user_test.rb index f027e7a525..21117bb380 100644 --- a/test/models/app_user_test.rb +++ b/test/models/app_user_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class AppUserTest < ActiveSupport::TestCase # test "the truth" do diff --git a/test/models/app_version_test.rb b/test/models/app_version_test.rb index 480ec3c568..5223cc5921 100644 --- a/test/models/app_version_test.rb +++ b/test/models/app_version_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class AppVersionTest < ActiveSupport::TestCase # test "the truth" do diff --git a/test/models/credential_test.rb b/test/models/credential_test.rb index 565023be5f..bd0f1567ab 100644 --- a/test/models/credential_test.rb +++ b/test/models/credential_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class CredentialTest < ActiveSupport::TestCase # test "the truth" do diff --git a/test/models/data_query_test.rb b/test/models/data_query_test.rb index eac372d5de..d229b51777 100644 --- a/test/models/data_query_test.rb +++ b/test/models/data_query_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class DataQueryTest < ActiveSupport::TestCase # test "the truth" do diff --git a/test/models/data_source_test.rb b/test/models/data_source_test.rb index 960825971b..33a4d85afc 100644 --- a/test/models/data_source_test.rb +++ b/test/models/data_source_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class DataSourceTest < ActiveSupport::TestCase # test "the truth" do diff --git a/test/models/data_source_user_oauth2_test.rb b/test/models/data_source_user_oauth2_test.rb index 4337b50531..ff361f7dc4 100644 --- a/test/models/data_source_user_oauth2_test.rb +++ b/test/models/data_source_user_oauth2_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class DataSourceUserOauth2Test < ActiveSupport::TestCase # test "the truth" do diff --git a/test/models/folder_app_test.rb b/test/models/folder_app_test.rb index a9092512ba..7c7b32cffa 100644 --- a/test/models/folder_app_test.rb +++ b/test/models/folder_app_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "test_helper" class FolderAppTest < ActiveSupport::TestCase diff --git a/test/models/folder_test.rb b/test/models/folder_test.rb index 501b50c62c..bbe7e5c307 100644 --- a/test/models/folder_test.rb +++ b/test/models/folder_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "test_helper" class FolderTest < ActiveSupport::TestCase diff --git a/test/models/metadatum_test.rb b/test/models/metadatum_test.rb index 8a2b7ed83b..eebdc9a820 100644 --- a/test/models/metadatum_test.rb +++ b/test/models/metadatum_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "test_helper" class MetadatumTest < ActiveSupport::TestCase diff --git a/test/models/organization_user_test.rb b/test/models/organization_user_test.rb index 8f272504c7..7de6ddf6ee 100644 --- a/test/models/organization_user_test.rb +++ b/test/models/organization_user_test.rb @@ -1,4 +1,6 @@ -require 'test_helper' +# frozen_string_literal: true + +require "test_helper" class OrganizationUserTest < ActiveSupport::TestCase # test "the truth" do diff --git a/test/test_helper.rb b/test/test_helper.rb index 2e7cbfe734..1fc320ab56 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,6 +1,8 @@ -ENV['RAILS_ENV'] ||= 'test' -require_relative '../config/environment' -require 'rails/test_help' +# frozen_string_literal: true + +ENV["RAILS_ENV"] ||= "test" +require_relative "../config/environment" +require "rails/test_help" class ActiveSupport::TestCase # Run tests in parallel with specified workers @@ -11,6 +13,6 @@ class ActiveSupport::TestCase def auth_header(user) token = JsonWebToken.encode(user_id: user.id) - { 'Authorization' => token } + { "Authorization" => token } end end