sql-jarvis 1.8.0 → 1.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,67 @@
1
+ module Blazer
2
+ module Adapters
3
+ class DruidAdapter < BaseAdapter
4
+ TIMESTAMP_REGEX = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\z/
5
+
6
+ def run_statement(statement, comment)
7
+ columns = []
8
+ rows = []
9
+ error = nil
10
+
11
+ header = {"Content-Type" => "application/json", "Accept" => "application/json"}
12
+ timeout = data_source.timeout ? data_source.timeout.to_i : 300
13
+ data = {
14
+ query: statement,
15
+ context: {
16
+ timeout: timeout * 1000
17
+ }
18
+ }
19
+
20
+ uri = URI.parse("#{settings["url"]}/druid/v2/sql/")
21
+ http = Net::HTTP.new(uri.host, uri.port)
22
+ http.read_timeout = timeout
23
+
24
+ begin
25
+ response = JSON.parse(http.post(uri.request_uri, data.to_json, header).body)
26
+ if response.is_a?(Hash)
27
+ error = response["errorMessage"] || "Unknown error: #{response.inspect}"
28
+ if error.include?("timed out")
29
+ error = Blazer::TIMEOUT_MESSAGE
30
+ end
31
+ else
32
+ columns = (response.first || {}).keys
33
+ rows = response.map { |r| r.values }
34
+
35
+ # Druid doesn't return column types
36
+ # and no timestamp type in JSON
37
+ rows.each do |row|
38
+ row.each_with_index do |v, i|
39
+ if v.is_a?(String) && TIMESTAMP_REGEX.match(v)
40
+ row[i] = Time.parse(v)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ rescue => e
46
+ error = e.message
47
+ end
48
+
49
+ [columns, rows, error]
50
+ end
51
+
52
+ def tables
53
+ result = data_source.run_statement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA') ORDER BY TABLE_NAME")
54
+ result.rows.map(&:first)
55
+ end
56
+
57
+ def schema
58
+ result = data_source.run_statement("SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA') ORDER BY 1, 2")
59
+ result.rows.group_by { |r| [r[0], r[1]] }.map { |k, vs| {schema: k[0], table: k[1], columns: vs.sort_by { |v| v[2] }.map { |v| {name: v[2], data_type: v[3]} }} }
60
+ end
61
+
62
+ def preview_statement
63
+ "SELECT * FROM {table} LIMIT 10"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,73 @@
1
+ module Blazer
2
+ module Adapters
3
+ class SnowflakeAdapter < SqlAdapter
4
+ def initialize(data_source)
5
+ @data_source = data_source
6
+
7
+ @@registered ||= begin
8
+ require "active_record/connection_adapters/odbc_adapter"
9
+ require "odbc_adapter/adapters/postgresql_odbc_adapter"
10
+
11
+ ODBCAdapter.register(/snowflake/, ODBCAdapter::Adapters::PostgreSQLODBCAdapter) do
12
+ # Explicitly turning off prepared statements as they are not yet working with
13
+ # snowflake + the ODBC ActiveRecord adapter
14
+ def prepared_statements
15
+ false
16
+ end
17
+
18
+ # Quoting needs to be changed for snowflake
19
+ def quote_column_name(name)
20
+ name.to_s
21
+ end
22
+
23
+ private
24
+
25
+ # Override dbms_type_cast to get the values encoded in UTF-8
26
+ def dbms_type_cast(columns, values)
27
+ int_column = {}
28
+ columns.each_with_index do |c, i|
29
+ int_column[i] = c.type == 3 && c.scale == 0
30
+ end
31
+
32
+ float_column = {}
33
+ columns.each_with_index do |c, i|
34
+ float_column[i] = c.type == 3 && c.scale != 0
35
+ end
36
+
37
+ values.each do |row|
38
+ row.each_index do |idx|
39
+ val = row[idx]
40
+ if val
41
+ if int_column[idx]
42
+ row[idx] = val.to_i
43
+ elsif float_column[idx]
44
+ row[idx] = val.to_f
45
+ elsif val.is_a?(String)
46
+ row[idx] = val.force_encoding('UTF-8')
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ @connection_model =
56
+ Class.new(Blazer::Connection) do
57
+ def self.name
58
+ "Blazer::Connection::SnowflakeAdapter#{object_id}"
59
+ end
60
+ if data_source.settings["conn_str"]
61
+ establish_connection(adapter: "odbc", conn_str: data_source.settings["conn_str"])
62
+ elsif data_source.settings["dsn"]
63
+ establish_connection(adapter: "odbc", dsn: data_source.settings["dsn"])
64
+ end
65
+ end
66
+ end
67
+
68
+ def cancel(run_id)
69
+ # todo
70
+ end
71
+ end
72
+ end
73
+ end
@@ -4,24 +4,13 @@ module Blazer
4
4
  class DataSource
5
5
  extend Forwardable
6
6
 
7
- attr_reader :id, :settings, :adapter, :adapter_instance
7
+ attr_reader :id, :settings
8
8
 
9
9
  def_delegators :adapter_instance, :schema, :tables, :preview_statement, :reconnect, :cost, :explain, :cancel
10
10
 
11
11
  def initialize(id, settings)
12
12
  @id = id
13
13
  @settings = settings
14
-
15
- unless settings["url"] || Rails.env.development? || ["bigquery", "athena"].include?(settings["adapter"])
16
- raise Blazer::Error, "Empty url for data source: #{id}"
17
- end
18
-
19
- @adapter_instance =
20
- if Blazer.adapters[adapter]
21
- Blazer.adapters[adapter].new(self)
22
- else
23
- raise Blazer::Error, "Unknown adapter"
24
- end
25
14
  end
26
15
 
27
16
  def adapter
@@ -101,7 +90,6 @@ module Blazer
101
90
  end
102
91
 
103
92
  def run_statement(statement, options = {})
104
- run_id = options[:run_id]
105
93
  async = options[:async]
106
94
  result = nil
107
95
  if cache_mode != "off"
@@ -154,9 +142,23 @@ module Blazer
154
142
 
155
143
  protected
156
144
 
145
+ def adapter_instance
146
+ @adapter_instance ||= begin
147
+ unless settings["url"] || Rails.env.development? || ["bigquery", "athena", "snowflake"].include?(settings["adapter"])
148
+ raise Blazer::Error, "Empty url for data source: #{id}"
149
+ end
150
+
151
+ unless Blazer.adapters[adapter]
152
+ raise Blazer::Error, "Unknown adapter"
153
+ end
154
+
155
+ Blazer.adapters[adapter].new(self)
156
+ end
157
+ end
158
+
157
159
  def run_statement_helper(statement, comment, run_id)
158
160
  start_time = Time.now
159
- columns, rows, error = @adapter_instance.run_statement(statement, comment)
161
+ columns, rows, error = adapter_instance.run_statement(statement, comment)
160
162
  duration = Time.now - start_time
161
163
 
162
164
  cache_data = nil
@@ -165,7 +167,7 @@ module Blazer
165
167
  cache_data = Marshal.dump([columns, rows, error, cache ? Time.now : nil]) rescue nil
166
168
  end
167
169
 
168
- if cache && cache_data && @adapter_instance.cachable?(statement)
170
+ if cache && cache_data && adapter_instance.cachable?(statement)
169
171
  Blazer.cache.write(statement_cache_key(statement), cache_data, expires_in: cache_expires_in.to_f * 60)
170
172
  end
171
173
 
@@ -183,7 +185,7 @@ module Blazer
183
185
  def detect_adapter
184
186
  schema = settings["url"].to_s.split("://").first
185
187
  case schema
186
- when "mongodb", "presto"
188
+ when "mongodb", "presto", "cassandra"
187
189
  schema
188
190
  else
189
191
  "sql"
data/lib/blazer/engine.rb CHANGED
@@ -11,21 +11,23 @@ module Blazer
11
11
  Blazer.audit = Blazer.settings.key?("audit") ? Blazer.settings["audit"] : true
12
12
  Blazer.user_name = Blazer.settings["user_name"] if Blazer.settings["user_name"]
13
13
  Blazer.from_email = Blazer.settings["from_email"] if Blazer.settings["from_email"]
14
- Blazer.before_action = Blazer.settings["before_action"] if Blazer.settings["before_action"]
15
-
16
- Blazer.user_class ||= Blazer.settings.key?("user_class") ? Blazer.settings["user_class"] : (User rescue nil)
17
- Blazer.user_method = Blazer.settings["user_method"]
18
- if Blazer.user_class
19
- Blazer.user_method ||= "current_#{Blazer.user_class.to_s.downcase.singularize}"
20
- end
21
-
14
+ Blazer.before_action = Blazer.settings["before_action_method"] if Blazer.settings["before_action_method"]
22
15
  Blazer.check_schedules = Blazer.settings["check_schedules"] if Blazer.settings.key?("check_schedules")
16
+
23
17
  if Blazer.settings.key?("mapbox_access_token")
24
18
  Blazer.mapbox_access_token = Blazer.settings["mapbox_access_token"]
25
19
  elsif ENV["MAPBOX_ACCESS_TOKEN"].present?
26
20
  Blazer.mapbox_access_token = ENV["MAPBOX_ACCESS_TOKEN"]
27
21
  end
28
22
 
23
+ if Blazer.settings.key?('assignees')
24
+ data_source = Blazer.data_sources['main']
25
+ statement = Blazer.settings['assignees']
26
+ Blazer.assignees = (Blazer::RunStatement.new.perform(data_source, statement, {}).rows rescue [])
27
+ else
28
+ Blazer.assignees = []
29
+ end
30
+
29
31
  if Blazer.user_class
30
32
  options = Blazer::BELONGS_TO_OPTIONAL.merge(class_name: Blazer.user_class.to_s)
31
33
  Blazer::Query.belongs_to :creator, options
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "1.8.0"
2
+ VERSION = "1.9.6"
3
3
  end
@@ -3,7 +3,6 @@
3
3
  data_sources:
4
4
  main:
5
5
  url: <%%= ENV["BLAZER_DATABASE_URL"] %>
6
- mapbox_access_token: <%%= ENV["MAPBOX_ACCESS_TOKEN"] %>
7
6
 
8
7
  # statement timeout, in seconds
9
8
  # none by default
@@ -48,13 +47,15 @@ audit: true
48
47
  # method name for the display name
49
48
  # user_name: name
50
49
 
51
- # optional auth method to use as a before_action (default: nil)
52
- # before_action: :authenticate!
50
+ # custom before_action to use for auth
51
+ # before_action_method: require_admin
53
52
 
54
53
  # email to send checks from
55
54
  # from_email: blazer@example.org
56
55
 
57
- # mapbox_access_token: ''
56
+ mapbox_access_token: <%= ENV["MAPBOX_ACCESS_TOKEN"] %>
57
+
58
+ #assignees: 'SELECT id, name FROM users ORDER BY id ASC'
58
59
 
59
60
  check_schedules:
60
61
  - "1 day"
@@ -6,6 +6,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
6
6
  t.text :description
7
7
  t.text :statement
8
8
  t.string :data_source
9
+ t.string :assignee_ids
9
10
  t.timestamps null: false
10
11
  end
11
12
 
@@ -1,6 +1,6 @@
1
1
  namespace :blazer do
2
2
  desc "run checks"
3
- task :run_checks, [:schedule] => :environment do |t, args|
3
+ task :run_checks, [:schedule] => :environment do |_, args|
4
4
  Blazer.run_checks(schedule: args[:schedule] || ENV["SCHEDULE"])
5
5
  end
6
6
 
Binary file
Binary file
metadata CHANGED
@@ -1,17 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sql-jarvis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - ThanhKhoaIT
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-03 00:00:00.000000000 Z
11
+ date: 2018-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - ">="
@@ -87,8 +101,11 @@ executables: []
87
101
  extensions: []
88
102
  extra_rdoc_files: []
89
103
  files:
104
+ - ".gitattributes"
105
+ - ".github/ISSUE_TEMPLATE.md"
90
106
  - ".gitignore"
91
107
  - CHANGELOG.md
108
+ - CONTRIBUTING.md
92
109
  - Gemfile
93
110
  - LICENSE.txt
94
111
  - README.md
@@ -120,6 +137,7 @@ files:
120
137
  - app/assets/javascripts/blazer/moment.js
121
138
  - app/assets/javascripts/blazer/queries.js
122
139
  - app/assets/javascripts/blazer/routes.js
140
+ - app/assets/javascripts/blazer/select2.js
123
141
  - app/assets/javascripts/blazer/selectize.js
124
142
  - app/assets/javascripts/blazer/stupidtable.js
125
143
  - app/assets/javascripts/blazer/vue.js
@@ -127,6 +145,7 @@ files:
127
145
  - app/assets/stylesheets/blazer/bootstrap.css.erb
128
146
  - app/assets/stylesheets/blazer/daterangepicker-bs3.css
129
147
  - app/assets/stylesheets/blazer/github.css
148
+ - app/assets/stylesheets/blazer/select2.min.css
130
149
  - app/assets/stylesheets/blazer/selectize.default.css
131
150
  - app/controllers/blazer/base_controller.rb
132
151
  - app/controllers/blazer/checks_controller.rb
@@ -167,10 +186,13 @@ files:
167
186
  - lib/blazer/adapters/athena_adapter.rb
168
187
  - lib/blazer/adapters/base_adapter.rb
169
188
  - lib/blazer/adapters/bigquery_adapter.rb
189
+ - lib/blazer/adapters/cassandra_adapter.rb
170
190
  - lib/blazer/adapters/drill_adapter.rb
191
+ - lib/blazer/adapters/druid_adapter.rb
171
192
  - lib/blazer/adapters/elasticsearch_adapter.rb
172
193
  - lib/blazer/adapters/mongodb_adapter.rb
173
194
  - lib/blazer/adapters/presto_adapter.rb
195
+ - lib/blazer/adapters/snowflake_adapter.rb
174
196
  - lib/blazer/adapters/sql_adapter.rb
175
197
  - lib/blazer/data_source.rb
176
198
  - lib/blazer/detect_anomalies.R
@@ -180,9 +202,11 @@ files:
180
202
  - lib/blazer/run_statement_job.rb
181
203
  - lib/blazer/version.rb
182
204
  - lib/generators/blazer/install_generator.rb
183
- - lib/generators/blazer/templates/config.yml
184
- - lib/generators/blazer/templates/install.rb
205
+ - lib/generators/blazer/templates/config.yml.tt
206
+ - lib/generators/blazer/templates/install.rb.tt
185
207
  - lib/tasks/blazer.rake
208
+ - sql-jarvis-1.9.4.gem
209
+ - sql-jarvis-1.9.5.gem
186
210
  homepage: https://github.com/ThanhKhoaIT/blazer
187
211
  licenses:
188
212
  - MIT
@@ -203,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
203
227
  version: '0'
204
228
  requirements: []
205
229
  rubyforge_project:
206
- rubygems_version: 2.6.11
230
+ rubygems_version: 2.6.14
207
231
  signing_key:
208
232
  specification_version: 4
209
233
  summary: Fork from ankane! Explore your data with SQL. Easily create charts and dashboards,