superset 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.buildkite/pipeline.yml +16 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +13 -0
  5. data/CHANGELOG.md +48 -0
  6. data/Dockerfile +17 -0
  7. data/LICENSE +21 -0
  8. data/README.md +205 -0
  9. data/Rakefile +12 -0
  10. data/doc/duplicate_dashboards.md +214 -0
  11. data/doc/setting_up_personal_api_credentials.md +127 -0
  12. data/docker-compose.override.yml +10 -0
  13. data/docker-compose.yml +8 -0
  14. data/env.sample +9 -0
  15. data/lib/loggers/duplicate_dashboard_logger.rb +15 -0
  16. data/lib/superset/authenticator.rb +55 -0
  17. data/lib/superset/chart/bulk_delete.rb +40 -0
  18. data/lib/superset/chart/delete.rb +30 -0
  19. data/lib/superset/chart/get.rb +56 -0
  20. data/lib/superset/chart/list.rb +59 -0
  21. data/lib/superset/chart/update_dataset.rb +90 -0
  22. data/lib/superset/client.rb +53 -0
  23. data/lib/superset/credential/api_user.rb +25 -0
  24. data/lib/superset/credential/embedded_user.rb +25 -0
  25. data/lib/superset/dashboard/bulk_delete.rb +42 -0
  26. data/lib/superset/dashboard/bulk_delete_cascade.rb +52 -0
  27. data/lib/superset/dashboard/charts/list.rb +47 -0
  28. data/lib/superset/dashboard/compare.rb +94 -0
  29. data/lib/superset/dashboard/copy.rb +78 -0
  30. data/lib/superset/dashboard/datasets/list.rb +74 -0
  31. data/lib/superset/dashboard/delete.rb +42 -0
  32. data/lib/superset/dashboard/embedded/get.rb +56 -0
  33. data/lib/superset/dashboard/embedded/put.rb +35 -0
  34. data/lib/superset/dashboard/export.rb +98 -0
  35. data/lib/superset/dashboard/get.rb +51 -0
  36. data/lib/superset/dashboard/info.rb +17 -0
  37. data/lib/superset/dashboard/list.rb +99 -0
  38. data/lib/superset/dashboard/put.rb +37 -0
  39. data/lib/superset/dashboard/warm_up_cache.rb +42 -0
  40. data/lib/superset/database/get.rb +30 -0
  41. data/lib/superset/database/get_schemas.rb +25 -0
  42. data/lib/superset/database/list.rb +51 -0
  43. data/lib/superset/dataset/bulk_delete.rb +41 -0
  44. data/lib/superset/dataset/create.rb +62 -0
  45. data/lib/superset/dataset/delete.rb +30 -0
  46. data/lib/superset/dataset/duplicate.rb +62 -0
  47. data/lib/superset/dataset/get.rb +56 -0
  48. data/lib/superset/dataset/list.rb +41 -0
  49. data/lib/superset/dataset/update_query.rb +56 -0
  50. data/lib/superset/dataset/update_schema.rb +120 -0
  51. data/lib/superset/dataset/warm_up_cache.rb +41 -0
  52. data/lib/superset/display.rb +42 -0
  53. data/lib/superset/enumerations/object_type.rb +11 -0
  54. data/lib/superset/file_utilities.rb +19 -0
  55. data/lib/superset/guest_token.rb +69 -0
  56. data/lib/superset/logger.rb +20 -0
  57. data/lib/superset/request.rb +62 -0
  58. data/lib/superset/route_info.rb +34 -0
  59. data/lib/superset/security/permissions_resources/list.rb +22 -0
  60. data/lib/superset/security/role/create.rb +25 -0
  61. data/lib/superset/security/role/get.rb +32 -0
  62. data/lib/superset/security/role/list.rb +45 -0
  63. data/lib/superset/security/role/permission/create.rb +35 -0
  64. data/lib/superset/security/role/permission/get.rb +37 -0
  65. data/lib/superset/security/user/create.rb +49 -0
  66. data/lib/superset/security/user/get.rb +27 -0
  67. data/lib/superset/security/user/list.rb +42 -0
  68. data/lib/superset/services/duplicate_dashboard.rb +298 -0
  69. data/lib/superset/sqllab/execute.rb +52 -0
  70. data/lib/superset/tag/add_to_object.rb +46 -0
  71. data/lib/superset/tag/get.rb +30 -0
  72. data/lib/superset/tag/list.rb +37 -0
  73. data/lib/superset/version.rb +5 -0
  74. data/lib/superset.rb +17 -0
  75. data/log/README.md +4 -0
  76. data/superset.gemspec +55 -0
  77. metadata +300 -0
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING: DESTRUCTIVE OPERATION .. use with caution
4
+ # This class is used to delete multiple dashboards and all related charts and datasets.
5
+ # There are NO CHECKS currently to confirm if a dataset is used on other dashboards.
6
+
7
+ module Superset
8
+ module Dashboard
9
+ class BulkDeleteCascade
10
+ class InvalidParameterError < StandardError; end
11
+
12
+ attr_reader :dashboard_ids
13
+
14
+ def initialize(dashboard_ids: [])
15
+ @dashboard_ids = dashboard_ids
16
+ end
17
+
18
+ def perform
19
+ raise InvalidParameterError, "dashboard_ids array of integers expected" unless dashboard_ids.is_a?(Array)
20
+ raise InvalidParameterError, "dashboard_ids array must contain Integer only values" unless dashboard_ids.all? { |item| item.is_a?(Integer) }
21
+
22
+ dashboard_ids.sort.each do |dashboard_id|
23
+ logger.info("Dashboard Id: #{dashboard_id.to_s} Attempting CASCADE delete of dashboard, charts, datasets")
24
+ delete_datasets(dashboard_id)
25
+ delete_charts(dashboard_id)
26
+ delete_dashboard(dashboard_id)
27
+ end
28
+ true
29
+ end
30
+
31
+ private
32
+
33
+ def delete_datasets(dashboard_id)
34
+ datasets_to_delete = Superset::Dashboard::Datasets::List.new(dashboard_id).datasets_details.map{|d| d[:id] }
35
+ Superset::Dataset::BulkDelete.new(dataset_ids: datasets_to_delete).perform if datasets_to_delete.any?
36
+ end
37
+
38
+ def delete_charts(dashboard_id)
39
+ charts_to_delete = Superset::Dashboard::Charts::List.new(dashboard_id).chart_ids
40
+ Superset::Chart::BulkDelete.new(chart_ids: charts_to_delete).perform if charts_to_delete.any?
41
+ end
42
+
43
+ def delete_dashboard(dashboard_id)
44
+ Superset::Dashboard::Delete.new(dashboard_id: dashboard_id, confirm_zero_charts: true).perform
45
+ end
46
+
47
+ def logger
48
+ @logger ||= Superset::Logger.new
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,47 @@
1
+ module Superset
2
+ module Dashboard
3
+ module Charts
4
+ class List < Superset::Request
5
+ attr_reader :id # dashboard id
6
+
7
+ def self.call(id)
8
+ self.new(id).list
9
+ end
10
+
11
+ def initialize(id)
12
+ @id = id
13
+ end
14
+
15
+ def chart_ids
16
+ result.map { |c| c[:id] }
17
+ end
18
+
19
+ def rows
20
+ result.map do |c|
21
+ [
22
+ c[:id],
23
+ c[:slice_name],
24
+ c[:form_data][:datasource],
25
+ # c[:form_data][:dashboards] # NOTE: form_data dashboards is not accurate .. looks to be bugs related to copying charts
26
+ ]
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def route
33
+ "dashboard/#{id}/charts"
34
+ end
35
+
36
+ def list_attributes
37
+ ['id', 'slice_name', 'datasource'].map(&:to_sym)
38
+ end
39
+
40
+ # when displaying a list of datasets, show dashboard id and title as well
41
+ def title
42
+ @title ||= [id, Superset::Dashboard::Get.new(id).title].join(' ')
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,94 @@
1
+ # A validation checker for comparing dashboards.
2
+ # This class is used to compare two dashboards by their datasets, charts, native filters and cross filters.
3
+ # Output is displayed in a table format to the ruby console
4
+ #
5
+ # Usage: Superset::Dashboard::Compare.new(first_dashboard_id: 322, second_dashboard_id: 347).perform
6
+ #
7
+ module Superset
8
+ module Dashboard
9
+ class Compare
10
+
11
+ attr_reader :first_dashboard_id, :second_dashboard_id
12
+
13
+ def initialize(first_dashboard_id: , second_dashboard_id: )
14
+ @first_dashboard_id = first_dashboard_id
15
+ @second_dashboard_id = second_dashboard_id
16
+ end
17
+
18
+
19
+ def perform
20
+ raise "Error: first_dashboard_id integer is required" unless first_dashboard_id.present? && first_dashboard_id.is_a?(Integer)
21
+ raise "Error: second_dashboard_id integer is required" unless second_dashboard_id.present? && second_dashboard_id.is_a?(Integer)
22
+
23
+ list_datasets
24
+ list_charts
25
+ list_native_filters
26
+ list_cross_filters
27
+
28
+ end
29
+
30
+ def first_dashboard
31
+ @first_dashboard ||= Get.new(first_dashboard_id).result
32
+ end
33
+
34
+ def second_dashboard
35
+ @second_dashboard ||= Get.new(second_dashboard_id).result
36
+ end
37
+
38
+ def list_datasets
39
+ puts "\n ====== DASHBOARD DATASETS ====== "
40
+ Superset::Dashboard::Datasets::List.new(first_dashboard_id).list
41
+ Superset::Dashboard::Datasets::List.new(second_dashboard_id).list
42
+ end
43
+
44
+ def list_charts
45
+ puts "\n ====== DASHBOARD CHARTS ====== "
46
+ Superset::Dashboard::Charts::List.new(first_dashboard_id).list
47
+ puts ''
48
+ Superset::Dashboard::Charts::List.new(second_dashboard_id).list
49
+ end
50
+
51
+ def list_native_filters
52
+ puts "\n ====== DASHBOARD NATIVE FILTERS ====== "
53
+ list_native_filters_for(first_dashboard)
54
+ puts ''
55
+ list_native_filters_for(second_dashboard)
56
+ end
57
+
58
+ def list_cross_filters
59
+ puts "\n ====== DASHBOARD CROSS FILTERS ====== "
60
+ list_cross_filters_for(first_dashboard)
61
+ puts ''
62
+ list_cross_filters_for(second_dashboard)
63
+ end
64
+
65
+ def native_filter_configuration(dashboard_result)
66
+ rows = []
67
+ JSON.parse(dashboard_result['json_metadata'])['native_filter_configuration'].each do |filter|
68
+ filter['targets'].each {|t| rows << [ t['column']['name'], t['datasetId'] ] }
69
+ end
70
+ rows
71
+ end
72
+
73
+ def list_native_filters_for(dashboard_result)
74
+ puts Terminal::Table.new(
75
+ title: [dashboard_result['id'], dashboard_result['dashboard_title']].join(' - '),
76
+ headings: ['Filter Name', 'Dataset Id'],
77
+ rows: native_filter_configuration(dashboard_result)
78
+ )
79
+ end
80
+
81
+ def cross_filter_configuration(dashboard_result)
82
+ JSON.parse(dashboard_result['json_metadata'])['chart_configuration'].map {|k, v| [ v['id'], v['crossFilters'].to_s ] }
83
+ end
84
+
85
+ def list_cross_filters_for(dashboard_result)
86
+ puts Terminal::Table.new(
87
+ title: [dashboard_result['id'], dashboard_result['dashboard_title']].join(' - '),
88
+ headings: ['Chart Id', 'Cross Filter Config'],
89
+ rows: cross_filter_configuration(dashboard_result)
90
+ )
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,78 @@
1
+
2
+ # TODO: some work to happen around TAGS still .. ie 'template' tag would indicate it tested and available to be copied.
3
+ # TODO: also need to ensoure that the embedded details are not duplicated across to the new dashboard
4
+
5
+ module Superset
6
+ module Dashboard
7
+ class Copy < Superset::Request
8
+
9
+ attr_reader :source_dashboard_id, :duplicate_slices, :clear_shared_label_colors
10
+
11
+ def initialize(source_dashboard_id: , duplicate_slices: false, clear_shared_label_colors: false)
12
+ @source_dashboard_id = source_dashboard_id
13
+ @duplicate_slices = duplicate_slices # boolean indicates whether to duplicate charts OR keep the new dashboard pointing to the same charts as the original
14
+ @clear_shared_label_colors = clear_shared_label_colors
15
+ end
16
+
17
+ def perform
18
+ raise "Error: source_dashboard_id integer is required" unless source_dashboard_id.present? && source_dashboard_id.is_a?(Integer)
19
+ raise "Error: duplicate_slices must be a boolean" unless duplicate_slices_is_boolean?
20
+
21
+ adjust_json_metadata
22
+ response
23
+ Superset::Dashboard::Get.new(id).perform # return the full new dashboard object
24
+ end
25
+
26
+ def params
27
+ {
28
+ "css" => "{}",
29
+ "dashboard_title" => "#{source_dashboard.title}",
30
+ "duplicate_slices" => duplicate_slices,
31
+ "json_metadata" => new_dashboard_json_metadata.to_json,
32
+ }
33
+ end
34
+
35
+ def response
36
+ @response ||= client.post(route, params)
37
+ end
38
+
39
+ def id
40
+ response["result"]["id"]
41
+ end
42
+
43
+ private
44
+
45
+ def route
46
+ "dashboard/#{source_dashboard_id}/copy/"
47
+ end
48
+
49
+ def adjust_json_metadata
50
+ # when copying a DB via the API, chart positions need to be nested under json_metadata according to the GUI copy function (as per dev tools investigation in browser)
51
+ new_dashboard_json_metadata.merge!( "positions" => source_dashboard.positions )
52
+
53
+ if clear_shared_label_colors
54
+ # if coping a dashboard to a new db schema .. shared label colors will not be relevant/match as they are specific to the previous schemas dataset values
55
+ new_dashboard_json_metadata.merge!( "shared_label_colors" => {} )
56
+ end
57
+ end
58
+
59
+ def source_dashboard
60
+ @source_dashboard ||= begin
61
+ dash = Get.new(source_dashboard_id)
62
+ dash.response
63
+ dash
64
+ rescue => e
65
+ raise "Error retrieving source dashboard #{e.message}"
66
+ end
67
+ end
68
+
69
+ def new_dashboard_json_metadata
70
+ @new_dashboard_json_metadata ||= source_dashboard.json_metadata
71
+ end
72
+
73
+ def duplicate_slices_is_boolean?
74
+ [true, false].include?(duplicate_slices)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,74 @@
1
+ # WARNING: Does not take into account datasets with queries that have embedded schema references.
2
+ # ie " select * from schema1.table join schema2.table" for a dataset query will ONLY return the datasets config schema setting not the sql query schema references which refers to 2 distinct schemas.
3
+ #
4
+ # WARNING: Does not return Filter Datasets for the dashboard
5
+
6
+ module Superset
7
+ module Dashboard
8
+ module Datasets
9
+ class List < Superset::Request
10
+ attr_reader :id # dashboard id
11
+
12
+ def self.call(id)
13
+ self.new(id).list
14
+ end
15
+
16
+ def initialize(id)
17
+ @id = id
18
+ end
19
+
20
+ def perform
21
+ response
22
+ self
23
+ end
24
+
25
+ def schemas
26
+ @schemas ||= begin
27
+ all_dashboard_schemas = result.map {|d| d[:schema] }.uniq
28
+
29
+ # For the current superset setup we will assume a dashboard datasets will point to EXACTLY one schema, their own.
30
+ # if not .. we need to know about it. Potentially we could override this check if others do not consider it a problem.
31
+ if all_dashboard_schemas.count > 1
32
+ Rollbar.error("SUPERSET DASHBOARD ERROR: Dashboard id #{id} has multiple dataset schema linked: #{all_dashboard_schemas.to_s}")
33
+ end
34
+ all_dashboard_schemas
35
+ end
36
+ end
37
+
38
+ def datasets_details
39
+ result.map do |details|
40
+ details.slice('id', 'datasource_name', 'schema', 'sql').merge('database' => details['database'].slice('id', 'name', 'backend')).with_indifferent_access
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def route
47
+ "dashboard/#{id}/datasets"
48
+ end
49
+
50
+ def list_attributes
51
+ ['id', 'datasource_name', 'database_id', 'database_name', 'database_backend', 'schema'].map(&:to_sym)
52
+ end
53
+
54
+ def rows
55
+ result.map do |d|
56
+ [
57
+ d[:id],
58
+ d[:datasource_name],
59
+ d[:database][:id],
60
+ d[:database][:name],
61
+ d[:database][:backend],
62
+ d[:schema]
63
+ ]
64
+ end
65
+ end
66
+
67
+ # when displaying a list of datasets, show dashboard title as well
68
+ def title
69
+ @title ||= [id, Superset::Dashboard::Get.new(id).title].join(' ')
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Superset
4
+ module Dashboard
5
+ class Delete < Superset::Request
6
+
7
+ attr_reader :dashboard_id, :confirm_zero_charts
8
+
9
+ def initialize(dashboard_id: , confirm_zero_charts: true)
10
+ @dashboard_id = dashboard_id
11
+ @confirm_zero_charts = confirm_zero_charts
12
+ end
13
+
14
+ def perform
15
+ raise InvalidParameterError, "dashboard_id integer is required" unless dashboard_id.present? && dashboard_id.is_a?(Integer)
16
+
17
+ confirm_zero_charts_on_dashboard if confirm_zero_charts
18
+
19
+ logger.info("Attempting to delete dashboard with id: #{dashboard_id}")
20
+ response
21
+ end
22
+
23
+ def response
24
+ @response ||= client.delete(route)
25
+ end
26
+
27
+ private
28
+
29
+ def confirm_zero_charts_on_dashboard
30
+ raise "Error: Dashboard includes #{dashboard_charts.count} charts. Please delete all charts before deleting the dashboard or override and set confirm_zero_charts: false" if dashboard_charts.count.positive?
31
+ end
32
+
33
+ def dashboard_charts
34
+ @dashboard_charts ||= Superset::Dashboard::Charts::List.new(dashboard_id).rows
35
+ end
36
+
37
+ def route
38
+ "dashboard/#{dashboard_id}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,56 @@
1
+ module Superset
2
+ module Dashboard
3
+ module Embedded
4
+ class Get < Superset::Request
5
+ attr_reader :id # dashboard id
6
+
7
+ def self.call(id)
8
+ self.new(id).list
9
+ end
10
+
11
+ def initialize(id)
12
+ @id = id
13
+ end
14
+
15
+ def response
16
+ @response ||= client.get(route)
17
+ rescue Happi::Error::NotFound => e
18
+ logger.info("Dashboard #{id} has no Embedded settings. (skipping)") # some dashboards don't have embedded settings, fine to ignore.
19
+ @response = { result: [] }.with_indifferent_access
20
+ @response
21
+ end
22
+
23
+ def result
24
+ response[:result].empty? ? [] : [ super ] # wrap single result in an array so it can be used in the tt list and table
25
+ end
26
+
27
+ def allowed_domains
28
+ result.first['allowed_domains'] unless response[:result].empty?
29
+ end
30
+
31
+ def uuid
32
+ result.first['uuid'] unless response[:result].empty?
33
+ end
34
+
35
+ def list
36
+ super unless response[:result].empty?
37
+ end
38
+
39
+ private
40
+
41
+ def route
42
+ "dashboard/#{id}/embedded"
43
+ end
44
+
45
+ def list_attributes
46
+ [:dashboard_id, :uuid, :allowed_domains, :changed_on]
47
+ end
48
+
49
+ # when displaying embedded details, show dashboard title as well
50
+ def title
51
+ Superset::Dashboard::Get.new(id).title
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,35 @@
1
+ module Superset
2
+ module Dashboard
3
+ module Embedded
4
+ class Put < Superset::Request
5
+ attr_reader :dashboard_id, :allowed_domains
6
+
7
+ def initialize(dashboard_id: , allowed_domains: )
8
+ @dashboard_id = dashboard_id
9
+ @allowed_domains = allowed_domains
10
+ end
11
+
12
+ def response
13
+ raise InvalidParameterError, 'dashboard_id integer is required' if dashboard_id.nil? || dashboard_id.class != Integer
14
+ raise InvalidParameterError, 'allowed_domains array is required' if allowed_domains.nil? || allowed_domains.class != Array
15
+
16
+ @response ||= client.put(route, params)
17
+ end
18
+
19
+ def params
20
+ { "allowed_domains": allowed_domains }
21
+ end
22
+
23
+ def uuid
24
+ result['uuid'] unless response[:result].empty?
25
+ end
26
+
27
+ private
28
+
29
+ def route
30
+ "dashboard/#{dashboard_id}/embedded"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,98 @@
1
+ # Will export the zip file to /tmp/superset_dashboards with zip filename adjusted to include the dashboard_id
2
+ # Example zipfile: dashboard_#{dashboard_id}_export_#{datestamp}.zip
3
+ # Will then unzip and copy the files into the destination_path with the dashboard_id as a subfolder
4
+ #
5
+ # Usage
6
+ # Superset::Dashboard::Export.new(dashboard_id: 15, destination_path: '/tmp/superset_dashboard_backups/').perform
7
+ #
8
+
9
+ require 'superset/file_utilities'
10
+
11
+ module Superset
12
+ module Dashboard
13
+ class Export < Request
14
+ include FileUtilities
15
+
16
+ TMP_SUPERSET_DASHBOARD_PATH = '/tmp/superset_dashboards'
17
+
18
+ attr_reader :dashboard_id, :destination_path
19
+
20
+ def initialize(dashboard_id: , destination_path: )
21
+ @dashboard_id = dashboard_id
22
+ @destination_path = destination_path.chomp('/')
23
+ end
24
+
25
+ def perform
26
+ create_tmp_dir
27
+ save_exported_zip_file
28
+ unzip_files
29
+ copy_export_files_to_destination_path if destination_path
30
+ end
31
+
32
+ def response
33
+ @response ||= client.call(
34
+ :get,
35
+ client.url(route),
36
+ client.param_check(params)
37
+ )
38
+ end
39
+
40
+ private
41
+
42
+ def params
43
+ { "q": "!(#{dashboard_id})" } # pulled off chrome dev tools doing a GUI export. Swagger interface not helpfull with this endpoint.
44
+ end
45
+
46
+ def save_exported_zip_file
47
+ File.open(zip_file_name, 'wb') { |fp| fp.write(response.body) }
48
+ end
49
+
50
+ def unzip_files
51
+ @extracted_files = unzip_file(zip_file_name, tmp_uniq_dashboard_path)
52
+ end
53
+
54
+ def download_folder
55
+ File.dirname(extracted_files[0])
56
+ end
57
+
58
+ def copy_export_files_to_destination_path
59
+ path_with_dash_id = File.join(destination_path, dashboard_id.to_s)
60
+ FileUtils.mkdir_p(path_with_dash_id) unless File.directory?(path_with_dash_id)
61
+
62
+ Dir.glob("#{download_folder}/*").each do |item|
63
+ FileUtils.cp_r(item, path_with_dash_id)
64
+ end
65
+ end
66
+
67
+ def zip_file_name
68
+ @zip_file_name ||= "#{tmp_uniq_dashboard_path}/dashboard_#{dashboard_id}_export_#{datestamp}.zip"
69
+ end
70
+
71
+ def create_tmp_dir
72
+ FileUtils.mkdir_p(tmp_uniq_dashboard_path) unless File.directory?(tmp_uniq_dashboard_path)
73
+ end
74
+
75
+ # uniq random tmp folder name for each export
76
+ # this will allow us to do a wildcard glop on the folder to get the files
77
+ def tmp_uniq_dashboard_path
78
+ @tmp_uniq_dashboard_path ||= File.join(TMP_SUPERSET_DASHBOARD_PATH, uuid)
79
+ end
80
+
81
+ def uuid
82
+ SecureRandom.uuid
83
+ end
84
+
85
+ def extracted_files
86
+ @extracted_files ||= []
87
+ end
88
+
89
+ def route
90
+ "dashboard/export/"
91
+ end
92
+
93
+ def datestamp
94
+ @datestamp ||= Time.now.strftime('%Y%m%d')
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,51 @@
1
+ module Superset
2
+ module Dashboard
3
+ class Get < Superset::Request
4
+
5
+ attr_reader :id
6
+
7
+ def initialize(id)
8
+ @id = id
9
+ end
10
+
11
+ def self.call(id)
12
+ self.new(id).list
13
+ end
14
+
15
+ def perform
16
+ response
17
+ self
18
+ end
19
+
20
+ def title
21
+ "#{result['dashboard_title']}"
22
+ end
23
+
24
+ def json_metadata
25
+ JSON.parse(result['json_metadata'])
26
+ end
27
+
28
+ def positions
29
+ JSON.parse(result['position_json'])
30
+ end
31
+
32
+ def url
33
+ "#{superset_host}#{result['url']}"
34
+ end
35
+
36
+ private
37
+
38
+ def route
39
+ "dashboard/#{id}"
40
+ end
41
+
42
+ def rows
43
+ result['charts'].map {|c| [c]}
44
+ end
45
+
46
+ def headings
47
+ ['Charts']
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ module Superset
2
+ module Dashboard
3
+ class Info < Superset::Request
4
+ alias result response
5
+
6
+ def filters
7
+ result['filters']
8
+ end
9
+
10
+ private
11
+
12
+ def route
13
+ "dashboard/_info"
14
+ end
15
+ end
16
+ end
17
+ end