superset 0.3.6 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a175ae90fbf33b12baafc49758b5c9b2e2869702a6be4f45849f66ac5a40afee
4
- data.tar.gz: bc9938edda2fd5879b1f1824116b572638ea8685dd7b4115ca3fbfba08cf6a3f
3
+ metadata.gz: 59ab63d9f8d51f73e9ca6606a0a559c75def26e9c69ab09eeeb59fd91cf24447
4
+ data.tar.gz: c7ae83dfa2a5b5f0e100d6cce0d38e7badf04e20d174c661207223493179f851
5
5
  SHA512:
6
- metadata.gz: 07d9b38d89bc252ea2a64b0cb835e9b9eefde88182636b4e8f900ccb7800e4e41f46fa3d15251ce3ba959f2805747f44b482d3b6b93c989ecbcb064d5d50ada9
7
- data.tar.gz: 77a8f91b04674504ec174ba04e166b30fd5e42e30ce0c7b9a5904944893108fd5a5ef8052c066082d476c79518dacd911c3660d246e9b6bd9bf9f11fafe6c9f7
6
+ metadata.gz: 9f0f70518ea7bc2f9e19ba9c115bdb1c2c44f05abc0e887fcfb0de1934499847841e601ec0d5e8aabb957c368196873cad0bae6ccb300ce55fe8d9eedd3c4e29
7
+ data.tar.gz: 3a90c54aca065f2187bf971912dc2993f25034f6c1880fb4d973a90da3b2a1d63188871acfe64d7448abd431f27a2ea52ecc91d1c9c55194f9815016b8872b61
data/AGENTS.md ADDED
@@ -0,0 +1,105 @@
1
+ # Agents
2
+
3
+ This file provides guidance to AI coding agents working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ bundle install
10
+
11
+ # Open interactive console (auto-loads .env)
12
+ bin/console
13
+
14
+ # Run full test suite
15
+ rspec
16
+
17
+ # Run a single test file
18
+ rspec spec/path/to/test_spec.rb
19
+
20
+ # Run a single test by line number
21
+ rspec spec/path/to/test_spec.rb:42
22
+
23
+ # Lint
24
+ rubocop
25
+
26
+ # Run specs + rubocop together
27
+ rake
28
+ ```
29
+
30
+ Docker equivalents: prefix commands with `docker-compose run --rm app`.
31
+
32
+ ### Environment Setup
33
+
34
+ Copy `env.sample` to `.env` and fill in:
35
+ - `SUPERSET_HOST` — API base URL (required)
36
+ - `SUPERSET_API_USERNAME` / `SUPERSET_API_PASSWORD` — API credentials (required)
37
+ - `SUPERSET_ENVIRONMENT` — optional; loads `.env-{ENVIRONMENT}` instead
38
+ - `CONSOLE_TIMEOUT` — seconds before auto-logout in console (default 1800)
39
+
40
+ ## Architecture
41
+
42
+ ### Request Framework
43
+
44
+ All API interactions inherit from `Superset::Request` (`lib/superset/request.rb`). Subclasses implement:
45
+ - `route` — returns the API path (e.g., `"dashboard/#{id}"`)
46
+ - `filters` — returns a filter query string or `""`
47
+ - `list_attributes` — array of field names used for terminal table display
48
+
49
+ **Patterns by HTTP verb:**
50
+ - **GET/LIST** — inherit `Superset::Request`, implement `route` and `filters`
51
+ - **PUT** — inherit `Superset::BasePutRequest`, takes `target_id:` and `params:`, implement `route`
52
+ - **DELETE/POST** — call `client.delete(route)` or `client.post(route, params)` directly
53
+
54
+ Pagination is built into `Superset::Request` (default page size: 100, max: 1000). List classes accept `page_num:` and `page_size:` kwargs.
55
+
56
+ ### Authentication Flow
57
+
58
+ 1. `Superset::Client` (extends `Happi::Client`) includes `Credential::ApiUser` and creates a `Superset::Authenticator`
59
+ 2. `Authenticator` POSTs to `api/v1/security/login` and extracts `access_token`
60
+ 3. `Client#connection` builds a Faraday connection with Bearer token; supports both JSON and multipart modes (`config.use_json`)
61
+
62
+ ### Query Filter Syntax
63
+
64
+ Superset uses an ORM-like filter string format:
65
+ ```
66
+ filters:!((col:column_name,opr:operation,value:value)),
67
+ ```
68
+ Common operations: `ct` (contains), `eq` (equals), `neq` (not equals), `rel_o_m`, `rel_m_m`, `dashboard_tags`.
69
+
70
+ ### Service Layer
71
+
72
+ `lib/superset/services/` contains complex multi-step workflows. The main one is `DuplicateDashboard`, which:
73
+ 1. Validates source/target IDs, schema existence, and data sovereignty rules (all chart datasets must share the same DB schema)
74
+ 2. Copies the dashboard and its charts
75
+ 3. Duplicates datasets pointing to the target schema
76
+ 4. Rewires chart JSON metadata and filter configs to the new dataset IDs
77
+ 5. Optionally sets embedded config, tags, and publishes
78
+ 6. **Rolls back all created objects on failure**
79
+
80
+ `ImportDashboardAcrossEnvironment` handles cross-environment migration via export/import ZIP files.
81
+
82
+ ### Display Mixin
83
+
84
+ `Superset::Display` (`lib/superset/display.rb`) is included in `Superset::Request` and provides `list`, `rows`, `to_h`, and `ids`. Requires `list_attributes` and `result` to be implemented by the subclass.
85
+
86
+ ### Resource–Relationship Model
87
+
88
+ - **Dashboards** contain **Charts**
89
+ - **Charts** reference **Datasets** (datasources)
90
+ - **Dashboard JSON metadata** stores layout, chart positions, and filter configurations
91
+ - **Filters** reference Datasets for filter values
92
+ - Duplication/migration requires updating all these cross-references
93
+
94
+ ### Conventions
95
+
96
+ - Namespace: `Superset::<ResourceType>::<Action>` (e.g., `Superset::Dashboard::List`)
97
+ - `perform` — executes a state-changing action, returns `self`
98
+ - `response` — raw (cached) HTTP response
99
+ - `result` — extracts `response['result']`
100
+ - Constructor filter params follow naming: `title_contains:`, `title_equals:`, etc.
101
+ - Logging goes through `Superset.logger`; by default writes to `log/superset-client.log`. Consumers can inject any `::Logger`-compatible object via `Superset.configure { |c| c.logger = your_logger }`.
102
+
103
+ ### Console Helpers
104
+
105
+ In `bin/console`: `sshelp` (alias: `superset_class_list`) lists all available `Superset::` classes. On startup, `Superset::Database::List.call` prints available DB connections.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## Changelog
2
2
 
3
+ ## 0.5.0 - 2026-07-21
4
+
5
+ * Decouple from Rollbar — remove the `Rollbar.error(...) if defined?(Rollbar)` call in `WarmUpCache`; exceptions now propagate to the caller so consumers' own error handling can react
6
+ * Introduce configurable logger via `Superset.configure { |c| c.logger = ... }` — any object responding to `#info` and `#error` (default remains `log/superset-client.log`)
7
+ * **Breaking:** `Superset::Dashboard::WarmUpCache.new(...).perform` now raises on per-dataset failures instead of swallowing them; wrap the call in your own `rescue` if you need the previous silent behavior
8
+ * Remove unused `DuplicateDashboardLogger` module (dead code)
9
+ * Drop `rollbar` dev dependency; drop `require "rollbar"` from `spec_helper.rb` and `bin/console`
10
+ * Add explicit `require 'json'` (the gem uses `to_json` in 8+ places and was relying on rollbar to transitively load it)
11
+
12
+ ## 0.4.0 - 2026-06-05
13
+
14
+ * send X-CSRFToken (and replay the session cookie) on state-changing requests so writes work against a CSRF-protected Superset
15
+ * send a same-origin Referer on state-changing requests to satisfy Flask-WTF WTF_CSRF_SSL_STRICT over HTTPS
16
+ * GuestToken: send X-CSRFToken + Referer (and replay the session cookie) on the guest_token POST — that endpoint is CSRF-protected too, so embedded dashboards broke without it
17
+ * add faraday-cookie_jar dependency
18
+
3
19
  ## 0.3.6 - 2026-02-26
4
20
 
5
21
  * add dry_run to dashboard bulk delete cascade #74
data/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ See [AGENTS.md](AGENTS.md) for project context and development instructions.
data/README.md CHANGED
@@ -44,6 +44,23 @@ Add to your Gemfile `gem 'superset'`
44
44
  And then execute: `bundle install`
45
45
  Or install it yourself as `gem install superset`
46
46
 
47
+ ## Configuration
48
+
49
+ The gem writes logs (dashboard duplication progress, API failures, etc.) through `Superset.logger`. By default it writes to `log/superset-client.log` relative to the working directory.
50
+
51
+ To route logs elsewhere — for example, STDOUT for a background worker, a null sink for tests, or your application's existing logger — configure it once at boot:
52
+
53
+ ```ruby
54
+ require 'logger'
55
+ require 'superset'
56
+
57
+ Superset.configure do |config|
58
+ config.logger = ::Logger.new($stdout) # or any object responding to #info(msg) and #error(msg)
59
+ end
60
+ ```
61
+
62
+ Any object that responds to `#info(msg)` and `#error(msg)` will work — a stdlib `::Logger`, a `SemanticLogger` instance, a framework-provided logger, or a custom wrapper that forwards to your error-reporting service. The gem does not assume any particular framework.
63
+
47
64
  ## Run specs
48
65
 
49
66
  ```
@@ -4,6 +4,8 @@ Superset API Credentials are essentially the username, password and host of the
4
4
 
5
5
  If you know these already, plug them and your host value into the `.env` and it should all just work. A sample env.sample is provided as a template.
6
6
 
7
+ Note: `.env` files are for local development only—do not store production credentials in a `.env` file (or commit it to git). In production, inject secrets at runtime via your platform's environment variables or a secret manager.
8
+
7
9
  Create your own .env file with
8
10
 
9
11
  ```
@@ -59,9 +61,17 @@ Scroll down to the 'Security Users' area and find the PUT request that will upda
59
61
 
60
62
  PUT `/api/v1/security/users/{pk}`
61
63
 
62
- Click 'Try it Out' and add your users ID in the PK input box.
64
+ Click 'Try it Out' and add your users ID in the PK input box.
65
+
66
+ Generate a secure random password with your preferred tool ... or in ruby with something like:
67
+ ```ruby
68
+ # ruby SecureRandom code to provide 32 random bytes
69
+ require 'securerandom'
70
+ SecureRandom.urlsafe_base64(32)
71
+ ```
72
+
73
+ Edit the params to only consist of only the password field and the value of your new password.
63
74
 
64
- Edit the params to only consist of only the password field and the value of your new password.
65
75
 
66
76
  ```
67
77
  {
@@ -159,5 +169,15 @@ Superset::GuestToken.new(embedded_dashboard_id: '15').guest_token
159
169
  => "eyJ0eXAiOi............VV4mrMfsvg"
160
170
  ```
161
171
 
172
+ ## Troubleshooting tips
162
173
 
174
+ ### Password or token expired
175
+
176
+ If your Superset password has expired (or your auth token is no longer valid), API calls may fail with an error like:
177
+
178
+ ```ruby
179
+ Superset::Dashboard::List.call
180
+ ArgumentError: Can't build an Authorization Bearer header from nil
181
+ ```
163
182
 
183
+ Fix: refresh your credentials via swagger interface then update your credentials in .env and try again.
@@ -1,7 +1,14 @@
1
+ require 'faraday-cookie_jar'
2
+
1
3
  module Superset
2
4
  class Client < Happi::Client
3
5
  include Credential::ApiUser
4
6
 
7
+ # Superset enforces CSRF on state-changing requests once WTF_CSRF_ENABLED is on;
8
+ # GETs are never CSRF-checked. Bearer-token auth is not sufficient,
9
+ # so these verbs must carry an X-CSRFToken header (see #call / #csrf_token).
10
+ CSRF_PROTECTED_METHODS = %i[post put patch delete].freeze
11
+
5
12
  attr_reader :authenticator
6
13
 
7
14
  def initialize
@@ -17,6 +24,15 @@ module Superset
17
24
  @superset_host ||= authenticator.superset_host
18
25
  end
19
26
 
27
+ # All verbs funnel through Happi::Client#call. Before any state-changing request,
28
+ # attach a CSRF token; fetching one also sets the Flask session cookie that the
29
+ # token is validated against, which the cookie jar on this connection replays on
30
+ # the write.
31
+ def call(method, url, params = {})
32
+ set_csrf_token if CSRF_PROTECTED_METHODS.include?(method)
33
+ super
34
+ end
35
+
20
36
  # TODO: Happi has not got a put method yet
21
37
  def put(resource, params = {})
22
38
  call(:put, url(resource), param_check(params))
@@ -40,9 +56,27 @@ module Superset
40
56
 
41
57
  private
42
58
 
59
+ # Set the CSRF headers for the upcoming write:
60
+ # * X-CSRFToken — session-bound token; GET /api/v1/security/csrf_token/ returns it
61
+ # and sets the Flask session cookie it is validated against (the cookie jar on
62
+ # this connection replays that cookie on the write).
63
+ # * Referer — over HTTPS, Flask-WTF's WTF_CSRF_SSL_STRICT (default True) also
64
+ # requires a Referer matching the Superset host (same-origin check), else the
65
+ # write fails with "400 The referrer header is missing." Browsers send this
66
+ # automatically; an API client must set it explicitly.
67
+ def set_csrf_token
68
+ connection.headers['X-CSRFToken'] = csrf_token
69
+ connection.headers['Referer'] = superset_host
70
+ end
71
+
72
+ def csrf_token
73
+ @csrf_token ||= get('security/csrf_token/')['result']
74
+ end
75
+
43
76
  def connection
44
77
  @connection ||= Faraday.new(superset_host) do |f|
45
78
  f.authorization :Bearer, access_token
79
+ f.use :cookie_jar # persist the Flask session cookie across the csrf_token GET and the write
46
80
  f.use FaradayMiddleware::ParseJson, content_type: 'application/json'
47
81
 
48
82
  if self.config.use_json
@@ -0,0 +1,11 @@
1
+ require "logger"
2
+
3
+ module Superset
4
+ class Configuration
5
+ attr_accessor :logger
6
+
7
+ def initialize
8
+ @logger = nil
9
+ end
10
+ end
11
+ end
@@ -78,6 +78,7 @@ module Superset
78
78
  def source_zip_file
79
79
  return source if zip?
80
80
 
81
+ require 'zip' # lazy: only import/export needs rubyzip
81
82
  Zip::File.open(new_zip_file, create: true) do |zipfile|
82
83
  Dir[File.join(source, "**", "**")].each do |file|
83
84
  next unless File.file?(file)
@@ -3,7 +3,7 @@ module Superset
3
3
  class WarmUpCache < Superset::Request
4
4
 
5
5
  attr_reader :dashboard_id
6
-
6
+
7
7
  def initialize(dashboard_id:)
8
8
  @dashboard_id = dashboard_id
9
9
  end
@@ -16,11 +16,7 @@ module Superset
16
16
  def response
17
17
  dataset_details = fetch_dataset_details(dashboard_id)
18
18
  dataset_details.each do |dataset|
19
- begin
20
- warm_up_dataset(dataset["datasource_name"], dataset["name"])
21
- rescue => e
22
- Rollbar.error("Warm up cache failed for the dashboard #{dashboard_id.to_s} and for the dataset #{dataset["datasource_name"]} - #{e}") if defined?(Rollbar)
23
- end
19
+ warm_up_dataset(dataset["datasource_name"], dataset["name"])
24
20
  end
25
21
  end
26
22
 
@@ -29,7 +25,7 @@ module Superset
29
25
  end
30
26
 
31
27
  private
32
-
28
+
33
29
  def validate_dashboard_id
34
30
  raise InvalidParameterError, "dashboard_id must be present and must be an integer" unless dashboard_id.present? && dashboard_id.is_a?(Integer)
35
31
  end
@@ -1,8 +1,9 @@
1
- require 'zip'
2
-
3
1
  module Superset
4
2
  module FileUtilities
3
+ # rubyzip is loaded lazily so the gem can be required without it; only
4
+ # consumers that actually import/export need rubyzip installed.
5
5
  def unzip_file(zip_file, destination)
6
+ require 'zip'
6
7
  entries = []
7
8
  Zip::File.open(zip_file) do |zip|
8
9
  zip.each do |entry|
@@ -1,3 +1,5 @@
1
+ require 'faraday-cookie_jar'
2
+
1
3
  module Superset
2
4
  class GuestToken
3
5
  include Credential::EmbeddedUser
@@ -52,13 +54,27 @@ module Superset
52
54
  'api/v1/security/guest_token/'
53
55
  end
54
56
 
57
+ # The guest_token endpoint is CSRF-protected (it is NOT in Superset's CSRF
58
+ # exempt list), so this POST needs the same treatment as Client writes:
59
+ # an X-CSRFToken bound to the session cookie, plus a same-origin
60
+ # Referer for WTF_CSRF_SSL_STRICT over HTTPS. The csrf_token GET also sets the
61
+ # session cookie that the cookie jar replays on the POST.
55
62
  def response
56
- @response ||= connection.post(route, params.to_json)
63
+ @response ||= begin
64
+ connection.headers['X-CSRFToken'] = csrf_token
65
+ connection.headers['Referer'] = authenticator.superset_host
66
+ connection.post(route, params.to_json)
67
+ end
68
+ end
69
+
70
+ def csrf_token
71
+ @csrf_token ||= connection.get('api/v1/security/csrf_token/').env.body['result']
57
72
  end
58
73
 
59
74
  def connection
60
75
  @connection ||= Faraday.new(authenticator.superset_host) do |f|
61
76
  f.authorization :Bearer, access_token
77
+ f.use :cookie_jar # replay the Flask session cookie from the csrf_token GET on the POST
62
78
  f.use FaradayMiddleware::ParseJson, content_type: 'application/json'
63
79
  f.request :json
64
80
  f.adapter :net_http
@@ -1,20 +1,11 @@
1
1
  module Superset
2
2
  class Logger
3
-
4
3
  def info(msg)
5
- # puts msg # allow logs to console
6
- logger.info msg
4
+ Superset.logger.info(msg)
7
5
  end
8
6
 
9
7
  def error(msg)
10
- # puts msg # allow logs to console
11
- logger.error msg
12
- end
13
-
14
- def logger
15
- @logger ||= begin
16
- ::Logger.new("log/superset-client.log")
17
- end
8
+ Superset.logger.error(msg)
18
9
  end
19
10
  end
20
11
  end
@@ -87,6 +87,7 @@ module Superset
87
87
  end
88
88
 
89
89
  def create_new_dashboard_zip
90
+ require 'zip' # lazy: only import/export needs rubyzip
90
91
  Zip::File.open(new_zip_file, create: true) do |zipfile|
91
92
  Dir[File.join(dashboard_export_root_path, '**', '**')].each do |file|
92
93
  zipfile.add(file.sub(dashboard_export_root_path + '/', File.basename(dashboard_export_root_path) + '/' ), file) if File.file?(file)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Superset
4
- VERSION = "0.3.6"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/superset.rb CHANGED
@@ -3,7 +3,10 @@
3
3
  require 'require_all'
4
4
  require 'terminal-table'
5
5
  require 'happi'
6
+ require 'logger'
7
+ require 'json'
6
8
 
9
+ require_relative "superset/configuration"
7
10
  require_rel "superset/credential"
8
11
  require_relative "superset/authenticator"
9
12
  require_relative "superset/client"
@@ -15,5 +18,31 @@ require_rel "superset"
15
18
 
16
19
  module Superset
17
20
  class Error < StandardError; end
18
- # Your code goes here...
21
+
22
+ DEFAULT_LOG_PATH = "log/superset-client.log"
23
+
24
+ class << self
25
+ def configuration
26
+ @configuration ||= Configuration.new
27
+ end
28
+
29
+ def configure
30
+ yield configuration
31
+ end
32
+
33
+ def logger
34
+ configuration.logger || default_logger
35
+ end
36
+
37
+ def reset_configuration!
38
+ @configuration = nil
39
+ @default_logger = nil
40
+ end
41
+
42
+ private
43
+
44
+ def default_logger
45
+ @default_logger ||= ::Logger.new(DEFAULT_LOG_PATH)
46
+ end
47
+ end
19
48
  end
data/superset.gemspec CHANGED
@@ -37,17 +37,22 @@ Gem::Specification.new do |spec|
37
37
  spec.add_dependency "json", ">= 2.0"
38
38
  spec.add_dependency "terminal-table", "~> 4.0"
39
39
  spec.add_dependency "require_all", ">= 3.0"
40
- spec.add_dependency "rubyzip", ">= 3.0"
41
40
  spec.add_dependency "faraday", "~> 1.0"
42
41
  spec.add_dependency "faraday-multipart", "~> 1.0"
42
+ spec.add_dependency "faraday-cookie_jar", "~> 0.0.7" # replay the Flask session cookie for CSRF
43
43
  spec.add_dependency "enumerate_it", ">= 1.7"
44
44
 
45
+ # rubyzip is only needed by the dashboard import/export feature, which lazily
46
+ # `require 'zip'` at call time. Kept as a dev dependency so those specs run here,
47
+ # but NOT a runtime dependency — consumers that don't import/export (embedders,
48
+ # read/write API clients) shouldn't inherit its rubyzip >= 3 pin.
49
+ # Apps that DO use import/export must declare rubyzip (>= 3.0) themselves.
50
+ spec.add_development_dependency "rubyzip", ">= 3.0"
45
51
  spec.add_development_dependency "dotenv", ">= 2.0"
46
52
  spec.add_development_dependency "rake", ">= 13.0"
47
53
  spec.add_development_dependency "rspec", ">= 3.0"
48
54
  spec.add_development_dependency "rubocop", ">= 1.0"
49
55
  spec.add_development_dependency "pry", ">= 0.14"
50
- spec.add_development_dependency "rollbar", ">= 3.0"
51
56
 
52
57
  # For more information and examples about making a new gem, check out our
53
58
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: superset
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - jbat
@@ -52,21 +52,21 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: '3.0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: rubyzip
55
+ name: faraday
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ">="
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '3.0'
60
+ version: '1.0'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ">="
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '3.0'
67
+ version: '1.0'
68
68
  - !ruby/object:Gem::Dependency
69
- name: faraday
69
+ name: faraday-multipart
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
@@ -80,19 +80,19 @@ dependencies:
80
80
  - !ruby/object:Gem::Version
81
81
  version: '1.0'
82
82
  - !ruby/object:Gem::Dependency
83
- name: faraday-multipart
83
+ name: faraday-cookie_jar
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '1.0'
88
+ version: 0.0.7
89
89
  type: :runtime
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '1.0'
95
+ version: 0.0.7
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: enumerate_it
98
98
  requirement: !ruby/object:Gem::Requirement
@@ -107,6 +107,20 @@ dependencies:
107
107
  - - ">="
108
108
  - !ruby/object:Gem::Version
109
109
  version: '1.7'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rubyzip
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '3.0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '3.0'
110
124
  - !ruby/object:Gem::Dependency
111
125
  name: dotenv
112
126
  requirement: !ruby/object:Gem::Requirement
@@ -177,20 +191,6 @@ dependencies:
177
191
  - - ">="
178
192
  - !ruby/object:Gem::Version
179
193
  version: '0.14'
180
- - !ruby/object:Gem::Dependency
181
- name: rollbar
182
- requirement: !ruby/object:Gem::Requirement
183
- requirements:
184
- - - ">="
185
- - !ruby/object:Gem::Version
186
- version: '3.0'
187
- type: :development
188
- prerelease: false
189
- version_requirements: !ruby/object:Gem::Requirement
190
- requirements:
191
- - - ">="
192
- - !ruby/object:Gem::Version
193
- version: '3.0'
194
194
  email:
195
195
  - jonathon.batson@gmail.com
196
196
  executables: []
@@ -201,7 +201,9 @@ files:
201
201
  - ".rspec"
202
202
  - ".rubocop.yml"
203
203
  - ".ruby-version"
204
+ - AGENTS.md
204
205
  - CHANGELOG.md
206
+ - CLAUDE.md
205
207
  - Dockerfile
206
208
  - LICENSE
207
209
  - README.md
@@ -214,7 +216,6 @@ files:
214
216
  - docker-compose.override.yml
215
217
  - docker-compose.yml
216
218
  - env.sample
217
- - lib/loggers/duplicate_dashboard_logger.rb
218
219
  - lib/superset.rb
219
220
  - lib/superset/authenticator.rb
220
221
  - lib/superset/base_put_request.rb
@@ -227,6 +228,7 @@ files:
227
228
  - lib/superset/chart/put.rb
228
229
  - lib/superset/chart/update_dataset.rb
229
230
  - lib/superset/client.rb
231
+ - lib/superset/configuration.rb
230
232
  - lib/superset/credential/api_user.rb
231
233
  - lib/superset/credential/embedded_user.rb
232
234
  - lib/superset/dashboard/bulk_delete.rb
@@ -1,15 +0,0 @@
1
- module Superset
2
- module DuplicateDashboardLogger
3
-
4
-
5
- def logger
6
- @logger ||= begin
7
- if defined?(Rails)
8
- Rails.try(:logger) || Logger.new(STDOUT)
9
- else
10
- Logger.new(STDOUT)
11
- end
12
- end
13
- end
14
- end
15
- end