Skip to content

Commit c8554f7

Browse files
authored
Merge pull request #21 from rdytech/NEP-16354-delete-cascade-endpoint
NEP-16354 add delete cascade endpoint
2 parents 3247a47 + aeddd4f commit c8554f7

File tree

7 files changed

+149
-22
lines changed

7 files changed

+149
-22
lines changed

lib/superset/dashboard/bulk_delete.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# 2 or do not have any charts or filters linked to them
77
# ( not sure if this needed at this point )
88

9+
# NOTE: deletes the Dashboard Only. Use Dashboard::BulkDeleteCascade to delete all related objects
910
module Superset
1011
module Dashboard
1112
class BulkDelete < Superset::Request
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/superset/dashboard/put.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ def perform
1717
raise "Error: params hash is required" unless params.present? && params.is_a?(Hash)
1818

1919
response
20-
Superset::Dashboard::Get.new(id).perform
2120
end
2221

2322
def response

lib/superset/services/duplicate_dashboard.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ module Superset
77
module Services
88
class DuplicateDashboard < Superset::Request
99

10-
attr_reader :source_dashboard_id, :target_schema, :target_database_id, :allowed_domains, :tags
10+
attr_reader :source_dashboard_id, :target_schema, :target_database_id, :allowed_domains, :tags, :publish
1111

12-
def initialize(source_dashboard_id:, target_schema:, target_database_id: , allowed_domains: [], tags: [])
12+
def initialize(source_dashboard_id:, target_schema:, target_database_id: , allowed_domains: [], tags: [], publish: false)
1313
@source_dashboard_id = source_dashboard_id
1414
@target_schema = target_schema
1515
@target_database_id = target_database_id
1616
@allowed_domains = allowed_domains
1717
@tags = tags
18+
@publish = publish
1819
end
1920

2021
def perform
@@ -43,10 +44,12 @@ def perform
4344

4445
add_tags_to_new_dashboard
4546

47+
publish_dashboard if publish
48+
4649
end_log_message
4750

4851
# return the new dashboard id and url
49-
{ new_dashboard_id: new_dashboard.id, new_dashboard_url: new_dashboard.url }
52+
{ new_dashboard_id: new_dashboard.id, new_dashboard_url: new_dashboard.url, published: publish }
5053

5154
rescue => e
5255
logger.error("#{e.message}")
@@ -153,6 +156,10 @@ def update_source_dashboard_json_metadata
153156
Superset::Dashboard::Put.new(target_dashboard_id: new_dashboard.id, params: { "json_metadata" => @new_dashboard_json_metadata_configuration.to_json }).perform
154157
end
155158

159+
def publish_dashboard
160+
Superset::Dashboard::Put.new(target_dashboard_id: new_dashboard.id, params: { published: publish } ).perform
161+
end
162+
156163
def new_dashboard
157164
@new_dashboard ||= begin
158165
copy = Superset::Dashboard::Copy.new(
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require 'spec_helper'
2+
3+
RSpec.describe Superset::Dashboard::BulkDeleteCascade do
4+
subject { described_class.new(dashboard_ids: dashboard_ids) }
5+
let(:dashboard_ids) { nil }
6+
7+
describe '.perform' do
8+
context 'when dashboard_ids is not present' do
9+
it 'raises an error' do
10+
expect { subject.perform }.to raise_error(Superset::Dashboard::BulkDeleteCascade::InvalidParameterError, "dashboard_ids array of integers expected")
11+
end
12+
end
13+
14+
context 'when dashboard_ids contains non integer values' do
15+
let(:dashboard_ids) { [1, 'string'] }
16+
17+
it 'raises an error' do
18+
expect { subject.perform }.to raise_error(Superset::Dashboard::BulkDeleteCascade::InvalidParameterError, "dashboard_ids array must contain Integer only values")
19+
end
20+
end
21+
22+
context 'when dashboard_ids are valid' do
23+
let(:dashboard_ids) { [1] }
24+
25+
before do
26+
allow(Superset::Dashboard::Datasets::List).to receive(:new).with(1).and_return(double(datasets_details: [{ id: 11 }, { id: 12 }]))
27+
allow(Superset::Dataset::BulkDelete).to receive(:new).with(dataset_ids: [11,12]).and_return(double(perform: true))
28+
29+
allow(Superset::Dashboard::Charts::List).to receive(:new).with(1).and_return(double(chart_ids: [21, 22]))
30+
allow(Superset::Chart::BulkDelete).to receive(:new).with(chart_ids: [21, 22]).and_return(double(perform: true))
31+
32+
allow(Superset::Dashboard::Delete).to receive(:new).with(dashboard_id: 1, confirm_zero_charts: true).and_return(double(perform: true))
33+
end
34+
35+
it 'bulk deletes related charts, datasets and the dashboard' do
36+
expect(subject.perform).to eq(true)
37+
end
38+
end
39+
end
40+
end

spec/superset/dashboard/put_spec.rb

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,16 @@
99
"positions" => { "key2" => "value2" }
1010
}.to_json
1111
}}
12-
13-
let(:new_dashboard_id) { 2 }
14-
let(:new_dashboard_instance) { instance_double("Superset::Dashboard::Get") }
12+
let(:response) { { "result" => { "id" => 2, "last_modified_time" => 1708484547.0 } } }
1513

1614
before do
17-
allow(subject).to receive(:response).and_return( { "result" =>
18-
{"id"=>new_dashboard_id, "last_modified_time"=>1708484547.0} }
19-
)
15+
allow(subject).to receive(:response).and_return(response)
2016
end
2117

2218
describe 'perform' do
2319
context 'with valid params' do
24-
before do
25-
allow(Superset::Dashboard::Get).to receive(:new).with(new_dashboard_id).and_return(new_dashboard_instance)
26-
allow(new_dashboard_instance).to receive(:perform).and_return(new_dashboard_instance)
27-
end
28-
29-
it 'returns the new dashboard object' do
30-
expect(subject.perform).to be new_dashboard_instance
20+
it 'returns response' do
21+
expect(subject.perform).to eq(response)
3122
end
3223
end
3324

spec/superset/services/duplicate_dashboard_spec.rb

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
context 'completes duplicate process' do
134134
context 'and returns the new dashboard details' do
135135
specify do
136-
expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2" })
136+
expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2", published: false})
137137
end
138138
end
139139

@@ -187,7 +187,7 @@
187187
expect(Superset::Dashboard::Embedded::Put).to_not receive(:new)
188188
end
189189

190-
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2" }) }
190+
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2", published: false}) }
191191
end
192192

193193
context 'are not empty' do
@@ -197,13 +197,13 @@
197197
expect(Superset::Dashboard::Embedded::Put).to receive(:new).with(dashboard_id: new_dashboard_id, allowed_domains: allowed_domains).and_return(double(result: { allowed_domains: allowed_domains }))
198198
end
199199

200-
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2" }) }
200+
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2", published: false}) }
201201
end
202202
end
203203

204204
context 'and tags' do
205205
context 'are empty' do
206-
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2" }) }
206+
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2", published: false}) }
207207
end
208208

209209
context 'are not empty' do
@@ -213,10 +213,47 @@
213213
expect(Superset::Tag::AddToObject).to receive(:new).with(object_type_id: ObjectType::DASHBOARD, object_id: new_dashboard_id, tags: tags).and_return(double(perform: true))
214214
end
215215

216-
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2" }) }
216+
specify { expect(subject.perform).to eq( { new_dashboard_id: 2, new_dashboard_url: "http://superset-host.com/superset/dashboard/2", published: false}) }
217217
end
218218
end
219219

220+
context 'and publish attr is' do
221+
context 'excluded from params' do
222+
before do
223+
expect(Superset::Dashboard::Put).to_not receive(:new).with(
224+
target_dashboard_id: new_dashboard_id,
225+
params: { published: false })
226+
end
227+
228+
specify 'defaults to false' do
229+
expect(subject.perform).to eq( {
230+
new_dashboard_id: 2,
231+
new_dashboard_url: "http://superset-host.com/superset/dashboard/2",
232+
published: false
233+
})
234+
end
235+
end
236+
237+
context 'included in params as true' do
238+
subject { described_class.new(
239+
source_dashboard_id: source_dashboard_id,
240+
target_schema: target_schema,
241+
target_database_id: target_database_id,
242+
publish: true ) }
243+
244+
before do
245+
expect(Superset::Dashboard::Put).to receive(:new).with(
246+
target_dashboard_id: new_dashboard_id,
247+
params: { published: true }).and_return(double(perform: true))
248+
end
249+
250+
specify { expect(subject.perform).to eq( {
251+
new_dashboard_id: 2,
252+
new_dashboard_url: "http://superset-host.com/superset/dashboard/2",
253+
published: true
254+
}) }
255+
end
256+
end
220257
end
221258

222259
context 'with invalid params' do

0 commit comments

Comments
 (0)