Skip to content

Commit be8c6ab

Browse files
authored
Merge pull request #15 from rdytech/NEP-17473-add-import-export-endpoints
[NEP-17473] Add Export endpoint
2 parents b204995 + e787672 commit be8c6ab

File tree

5 files changed

+159
-0
lines changed

5 files changed

+159
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ superset-*.gem
1717

1818
# ignore local log file
1919
log/superset-client.log
20+
21+
# ignore local ./tmp directory
22+
./tmp/

lib/superset/dashboard/export.rb

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/superset/file_utilities.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'zip'
2+
3+
module Superset
4+
module FileUtilities
5+
def unzip_file(zip_file, destination)
6+
entries = []
7+
Zip::File.open(zip_file) do |zip|
8+
zip.each do |entry|
9+
entry_path = File.join(destination, entry.name)
10+
entries << entry_path
11+
FileUtils.mkdir_p(File.dirname(entry_path))
12+
zip.extract(entry, entry_path)
13+
end
14+
end
15+
puts entries
16+
entries # return array of extracted files
17+
end
18+
end
19+
end
16.7 KB
Binary file not shown.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
require 'spec_helper'
2+
3+
RSpec.describe Superset::Dashboard::Export do
4+
subject { described_class.new(dashboard_id: dashboard_id, destination_path: destination_path) }
5+
let(:dashboard_id) { 18 }
6+
let(:destination_path) { './tmp/superset_dashboard_backups/' }
7+
8+
describe '#perform' do
9+
let(:response) { double('response') }
10+
let(:zip_file_name) { 'spec/fixtures/dashboard_18_export_20240322.zip' } # example birth names dashboard export fixture
11+
12+
before do
13+
allow(subject).to receive(:response).and_return(response)
14+
allow(subject).to receive(:zip_file_name).and_return(zip_file_name)
15+
allow(subject).to receive(:save_exported_zip_file)
16+
end
17+
18+
it 'unzips into the destination path' do
19+
subject.perform
20+
expect(Dir.glob(subject.destination_path + "/**/*").sort).to match_array([
21+
"./tmp/superset_dashboard_backups/18",
22+
"./tmp/superset_dashboard_backups/18/charts",
23+
"./tmp/superset_dashboard_backups/18/charts/Boy_Name_Cloud_53920.yaml",
24+
"./tmp/superset_dashboard_backups/18/charts/Names_Sorted_by_Num_in_California_53929.yaml",
25+
"./tmp/superset_dashboard_backups/18/charts/Number_of_Girls_53930.yaml",
26+
"./tmp/superset_dashboard_backups/18/charts/Pivot_Table_53931.yaml",
27+
"./tmp/superset_dashboard_backups/18/charts/Top_10_Girl_Name_Share_53921.yaml",
28+
"./tmp/superset_dashboard_backups/18/dashboards",
29+
"./tmp/superset_dashboard_backups/18/dashboards/Birth_Names_18.yaml",
30+
"./tmp/superset_dashboard_backups/18/databases",
31+
"./tmp/superset_dashboard_backups/18/databases/examples.yaml",
32+
"./tmp/superset_dashboard_backups/18/datasets",
33+
"./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+
])
37+
end
38+
end
39+
end

0 commit comments

Comments
 (0)