Skip to content

Commit 799ec8d

Browse files
committed
NEP-18768 API Dashboard Report
1 parent a3a3095 commit 799ec8d

File tree

4 files changed

+149
-11
lines changed

4 files changed

+149
-11
lines changed

lib/superset/dashboard/datasets/list.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,6 @@ def datasets_details
4141
end
4242
end
4343

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-
5444
def rows
5545
result.map do |d|
5646
[
@@ -64,6 +54,16 @@ def rows
6454
end
6555
end
6656

57+
private
58+
59+
def route
60+
"dashboard/#{id}/datasets"
61+
end
62+
63+
def list_attributes
64+
['id', 'datasource_name', 'database_id', 'database_name', 'database_backend', 'schema'].map(&:to_sym)
65+
end
66+
6767
# when displaying a list of datasets, show dashboard title as well
6868
def title
6969
@title ||= [id, Superset::Dashboard::Get.new(id).title].join(' ')

lib/superset/dataset/get.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def database_id
3838
end
3939

4040
def sql
41-
['sql']
41+
result['sql']
4242
end
4343

4444
private

lib/superset/display.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ def rows
1818
end
1919
end
2020

21+
def rows_hash
22+
rows.map { |value| list_attributes.zip(value).to_h }
23+
end
24+
2125
def title
2226
self.class.to_s
2327
end
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# Creates a log report on a set of dashboards
2+
# providing count of charts, datasets, and databases used in each dashboard
3+
# as well as optional data sovereignty information
4+
5+
module Superset
6+
module Services
7+
class DashboardReport
8+
9+
attr_reader :dashboard_ids, :report_on_data_sovereignty_only
10+
11+
def initialize(dashboard_ids: [], report_on_data_sovereignty_only: true)
12+
@dashboard_ids = dashboard_ids.presence || all_dashboard_ids
13+
@report_on_data_sovereignty_only = report_on_data_sovereignty_only
14+
end
15+
16+
def perform
17+
create_report
18+
19+
report_on_data_sovereignty_only ? display_data_sovereignty_report : @report
20+
end
21+
22+
def all_dashboard_ids
23+
Superset::Dashboard::List.new().rows.map{|d| d['id']}
24+
end
25+
26+
def display_data_sovereignty_report
27+
# filter by dashboards where
28+
# 1. A filter dataset is not part of the dashboard datasets (might be ok for some cases)
29+
# 2. There is more than one distinct dataset schema (never ok for embedded dashboards)
30+
31+
puts "Data Sovereignty Report"
32+
puts "-----------------------"
33+
puts "Invalid Dashboards: #{data_sovereignty_issues.count}"
34+
data_sovereignty_issues
35+
end
36+
37+
# possible data sovereignty issues
38+
def data_sovereignty_issues
39+
@report.map do |dashboard|
40+
reasons = []
41+
chart_dataset_ids = dashboard[:datasets][:chart_datasets].map{|d| d[:id]}
42+
43+
# invalid if any filters datasets are not part of the chart datasets
44+
unknown_datasets = dashboard[:filters][:filter_dataset_ids] - chart_dataset_ids
45+
if unknown_datasets.any?
46+
reasons << "WARNING: One or more filter datasets is not included in chart datasets for " \
47+
"filter dataset ids: #{unknown_datasets.join(', ')}."
48+
reasons << "DETAILS: #{unknown_dataset_details(unknown_datasets)}"
49+
end
50+
51+
# invalid if any filters datasets are not part of the chart datasets
52+
chart_dataset_schemas = dashboard[:datasets][:chart_datasets].map{|d| d[:schema]}.uniq
53+
if chart_dataset_schemas.count > 1
54+
reasons << "ERROR: Multiple distinct chart dataset schemas found. Expected 1. Found #{chart_dataset_schemas.count}. " \
55+
"schema names: #{chart_dataset_schemas.join(', ') }"
56+
end
57+
58+
{ reasons: reasons, dashboard: dashboard } if reasons.any?
59+
end.compact
60+
end
61+
62+
def unknown_dataset_details(unknown_datasets)
63+
unknown_datasets.map do |dataset_id|
64+
d = Superset::Dataset::Get.new(dataset_id)
65+
d.result
66+
{ id: d.id, name: d.title }
67+
rescue Happi::Error::NotFound => e
68+
{ id: dataset_id, name: '>>>> ERROR: DATASET DOES NOT EXIST <<<<' }
69+
rescue => e
70+
{ id: dataset_id, name: '>>>> ERROR: #{e} <<<<' }
71+
end
72+
end
73+
74+
def create_report
75+
@report ||= begin
76+
dashboard_ids.map do |dashboard_id|
77+
dashboard = dashboard_result(dashboard_id)
78+
# binding.pry
79+
{
80+
dashboard_id: dashboard_id,
81+
dashboard_title: dashboard['dashboard_title'],
82+
dashboard_url: dashboard['url'],
83+
dashboard_client_tags: dashboard_client_tags(dashboard),
84+
filters: filter_details(dashboard),
85+
charts: chart_count(dashboard),
86+
datasets: dataset_details(dashboard_id),
87+
}
88+
end
89+
end
90+
end
91+
92+
def filter_details(dashboard)
93+
{
94+
filter_count: filter_count(dashboard),
95+
filter_dataset_ids: filter_datasets(dashboard)
96+
}
97+
end
98+
99+
def filter_count(dashboard)
100+
dashboard['json_metadata']['native_filter_configuration'].count
101+
end
102+
103+
def filter_datasets(dashboard)
104+
dashboard['json_metadata']['native_filter_configuration'].map do |filter|
105+
filter['targets'].map{|d| d['datasetId']} if filter['type'] == 'NATIVE_FILTER'
106+
end.flatten.compact.uniq
107+
end
108+
109+
def chart_count(dashboard)
110+
dashboard['json_metadata']['chart_configuration'].count
111+
end
112+
113+
def dataset_details(dashboard_id)
114+
datasets = Superset::Dashboard::Datasets::List.new(dashboard_id).rows_hash
115+
{
116+
dataset_count: datasets.count,
117+
chart_datasets: datasets
118+
}
119+
end
120+
121+
def dashboard_client_tags(dashboard)
122+
dashboard['tags'].map { |t| t['name'] if t['name'].include?('client') }.compact.join(',') || 'None'
123+
end
124+
125+
def dashboard_result(dashboard_id)
126+
# convert json_metadata within result to a hash
127+
board = Superset::Dashboard::Get.new(dashboard_id)
128+
board.result['json_metadata'] = JSON.parse(board.result['json_metadata'])
129+
board.result['url'] = board.url # add full url to the dashboard result
130+
board.result
131+
end
132+
end
133+
end
134+
end

0 commit comments

Comments
 (0)