Skip to content

Commit 98064fc

Browse files
authored
Merge pull request #40 from rdytech/NEP-17596_export_dashboards_should_save_zip_file_and_clean_out_prev_ver
[NEP-17596] Superset: Export Dashboards should save zip file and clean out prev version totally
2 parents a3a3095 + 408b526 commit 98064fc

File tree

2 files changed

+103
-11
lines changed

2 files changed

+103
-11
lines changed

lib/superset/dashboard/export.rb

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,18 @@ def initialize(dashboard_id: , destination_path: )
2323
end
2424

2525
def perform
26+
logger.info("Exporting dashboard: #{dashboard_id}")
2627
create_tmp_dir
2728
save_exported_zip_file
2829
unzip_files
30+
clean_destination_directory
2931
copy_export_files_to_destination_path if destination_path
32+
33+
Dir.glob("#{destination_path_with_dash_id}/**/*").select { |f| File.file?(f) }
34+
rescue StandardError => e
35+
raise
36+
ensure
37+
cleanup_temp_dir
3038
end
3139

3240
def response
@@ -37,43 +45,67 @@ def response
3745
)
3846
end
3947

48+
def zip_file_name
49+
@zip_file_name ||= "#{tmp_uniq_dashboard_path}/dashboard_#{dashboard_id}_export_#{datestamp}.zip"
50+
end
51+
4052
private
4153

4254
def params
4355
{ "q": "!(#{dashboard_id})" } # pulled off chrome dev tools doing a GUI export. Swagger interface not helpfull with this endpoint.
4456
end
4557

4658
def save_exported_zip_file
59+
logger.info("Saving zip file: #{zip_file_name}")
4760
File.open(zip_file_name, 'wb') { |fp| fp.write(response.body) }
4861
end
4962

5063
def unzip_files
64+
logger.info("Unzipping file: #{zip_file_name}")
5165
@extracted_files = unzip_file(zip_file_name, tmp_uniq_dashboard_path)
5266
end
5367

5468
def download_folder
5569
File.dirname(extracted_files[0])
5670
end
5771

72+
def destination_path_with_dash_id
73+
@destination_path_with_dash_id ||= File.join(destination_path, dashboard_id.to_s)
74+
end
75+
76+
def clean_destination_directory
77+
logger.info("Cleaning destination directory: #{destination_path_with_dash_id}")
78+
if Dir.exist?(destination_path_with_dash_id)
79+
FileUtils.rm_rf(Dir.glob("#{destination_path_with_dash_id}/*"))
80+
else
81+
FileUtils.mkdir_p(destination_path_with_dash_id)
82+
end
83+
end
84+
5885
def copy_export_files_to_destination_path
86+
logger.info("Copying files to destination: #{destination_path_with_dash_id}")
5987
path_with_dash_id = File.join(destination_path, dashboard_id.to_s)
6088
FileUtils.mkdir_p(path_with_dash_id) unless File.directory?(path_with_dash_id)
89+
FileUtils.cp(zip_file_name, path_with_dash_id)
6190

6291
Dir.glob("#{download_folder}/*").each do |item|
6392
FileUtils.cp_r(item, path_with_dash_id)
6493
end
6594
end
6695

67-
def zip_file_name
68-
@zip_file_name ||= "#{tmp_uniq_dashboard_path}/dashboard_#{dashboard_id}_export_#{datestamp}.zip"
96+
def cleanup_temp_dir
97+
if Dir.exist?(tmp_uniq_dashboard_path)
98+
FileUtils.rm_rf(tmp_uniq_dashboard_path)
99+
end
69100
end
70101

71102
def create_tmp_dir
103+
logger.info("Creating tmp directory: #{tmp_uniq_dashboard_path}")
72104
FileUtils.mkdir_p(tmp_uniq_dashboard_path) unless File.directory?(tmp_uniq_dashboard_path)
73105
end
74106

75107
# 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
108+
# this will allow us to do a wildcard glop on the folder to get the files
77109
def tmp_uniq_dashboard_path
78110
@tmp_uniq_dashboard_path ||= File.join(TMP_SUPERSET_DASHBOARD_PATH, uuid)
79111
end
@@ -93,6 +125,25 @@ def route
93125
def datestamp
94126
@datestamp ||= Time.now.strftime('%Y%m%d')
95127
end
128+
129+
def unzip_file(zip_path, destination)
130+
extracted_files = []
131+
Zip::File.open(zip_path) do |zip_file|
132+
zip_file.each do |entry|
133+
entry_path = File.join(destination, entry.name)
134+
FileUtils.mkdir_p(File.dirname(entry_path))
135+
zip_file.extract(entry, entry_path) unless File.exist?(entry_path)
136+
extracted_files << entry_path
137+
end
138+
end
139+
extracted_files
140+
rescue => e
141+
raise
142+
end
143+
end
144+
145+
def logger
146+
@logger ||= Superset::Logger.new
96147
end
97148
end
98149
end

spec/superset/dashboard/export_spec.rb

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,55 @@
22

33
RSpec.describe Superset::Dashboard::Export do
44
subject { described_class.new(dashboard_id: dashboard_id, destination_path: destination_path) }
5+
56
let(:dashboard_id) { 18 }
67
let(:destination_path) { './tmp/superset_dashboard_backups/' }
8+
let(:datestamp) { '20240322' }
9+
let(:zip_file_name) { 'spec/fixtures/dashboard_18_export_20240322.zip' } # Example fixture zip file
710

811
describe '#perform' do
912
let(:response) { double('response') }
10-
let(:zip_file_name) { 'spec/fixtures/dashboard_18_export_20240322.zip' } # example birth names dashboard export fixture
1113

1214
before do
15+
FileUtils.rm_rf("#{destination_path}#{dashboard_id}") if File.directory?("#{destination_path}#{dashboard_id}")
16+
1317
allow(subject).to receive(:response).and_return(response)
1418
allow(subject).to receive(:zip_file_name).and_return(zip_file_name)
1519
allow(subject).to receive(:save_exported_zip_file)
20+
21+
allow(subject).to receive(:datestamp).and_return(datestamp)
22+
23+
@temp_dir = Dir.mktmpdir("superset_dashboard_exports")
24+
@extracted_files = [
25+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "metadata.yaml"),
26+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "dashboards", "Birth_Names_18.yaml"),
27+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "charts", "Boy_Name_Cloud_53920.yaml"),
28+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "charts", "Names_Sorted_by_Num_in_California_53929.yaml"),
29+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "charts", "Number_of_Girls_53930.yaml"),
30+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "charts", "Pivot_Table_53931.yaml"),
31+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "charts", "Top_10_Girl_Name_Share_53921.yaml"),
32+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "databases", "examples.yaml"),
33+
File.join(@temp_dir, "dashboard_export_#{datestamp}T123456", "datasets", "examples", "video_game_sales.yaml")
34+
]
35+
36+
FileUtils.mkdir_p(File.dirname(@extracted_files.first)) # Creates dashboard_export_20240322T123456
37+
@extracted_files.each do |file_path|
38+
FileUtils.mkdir_p(File.dirname(file_path))
39+
File.write(file_path, "dummy content for #{File.basename(file_path)}")
40+
end
41+
42+
allow(subject).to receive(:unzip_file).and_return(@extracted_files)
1643
end
1744

18-
it 'unzips into the destination path' do
45+
after do
46+
FileUtils.rm_rf(@temp_dir) if Dir.exist?(@temp_dir)
47+
FileUtils.rm_rf("#{destination_path}#{dashboard_id}") if File.directory?("#{destination_path}#{dashboard_id}")
48+
end
49+
50+
it 'unzips into the destination path with versioned filenames' do
1951
subject.perform
20-
expect(Dir.glob(subject.destination_path + "/**/*").sort).to match_array([
21-
"./tmp/superset_dashboard_backups/18",
52+
53+
expected_files = [
2254
"./tmp/superset_dashboard_backups/18/charts",
2355
"./tmp/superset_dashboard_backups/18/charts/Boy_Name_Cloud_53920.yaml",
2456
"./tmp/superset_dashboard_backups/18/charts/Names_Sorted_by_Num_in_California_53929.yaml",
@@ -31,9 +63,18 @@
3163
"./tmp/superset_dashboard_backups/18/databases/examples.yaml",
3264
"./tmp/superset_dashboard_backups/18/datasets",
3365
"./tmp/superset_dashboard_backups/18/datasets/examples",
34-
"./tmp/superset_dashboard_backups/18/datasets/examples/birth_names.yaml",
35-
"./tmp/superset_dashboard_backups/18/metadata.yaml"
36-
])
66+
"./tmp/superset_dashboard_backups/18/datasets/examples/video_game_sales.yaml",
67+
"./tmp/superset_dashboard_backups/18/metadata.yaml",
68+
"./tmp/superset_dashboard_backups/18/dashboard_18_export_20240322.zip" # Include the zip file itself
69+
]
70+
71+
actual_files = Dir.glob("#{destination_path}#{dashboard_id}/**/*").sort.map do |path|
72+
Pathname.new(path).relative_path_from(Pathname.new('.')).to_s
73+
end
74+
75+
actual_files = actual_files.map { |path| "./#{path}" }
76+
77+
expect(actual_files).to match_array(expected_files)
3778
end
3879
end
39-
end
80+
end

0 commit comments

Comments
 (0)