Skip to content

Commit 5e40a8f

Browse files
authored
Merge pull request #34 from rdytech/NEP-18440-add-create-chart-endpoint
NEP-18440 add create chart endpoint
2 parents 864b931 + b582d2f commit 5e40a8f

File tree

7 files changed

+164
-7
lines changed

7 files changed

+164
-7
lines changed

lib/superset/chart/create.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=begin
2+
Create a new chart from a set of params
3+
Suggestion is to base your params of an existing charts params and then modify them as needed
4+
So .. why not call the Superset::Chart::Duplicate class which then calls this Chart::Create class
5+
6+
This class is a bit more generic and can be used to create a new chart from scratch (if your confident in the params)
7+
8+
Usage:
9+
Superset::Chart::Create.new(params: new_chart_params).perform
10+
=end
11+
12+
module Superset
13+
module Chart
14+
class Create < Superset::Request
15+
16+
attr_reader :params
17+
18+
def initialize(params: )
19+
@params = params
20+
end
21+
22+
def perform
23+
raise "Error: params hash is required" unless params.present? && params.is_a?(Hash)
24+
25+
logger.info("Creating New Chart")
26+
response['id']
27+
end
28+
29+
def response
30+
@response ||= client.post(route, params)
31+
end
32+
33+
private
34+
35+
def route
36+
"chart/"
37+
end
38+
end
39+
end
40+
end

lib/superset/chart/duplicate.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# There is no API endpoint to duplicate charts in Superset.
2+
# This class is a workaround.
3+
# Requires a source chart id, target dataset id
4+
5+
module Superset
6+
module Chart
7+
class Duplicate < Superset::Request
8+
9+
attr_reader :source_chart_id, :target_dataset_id, :new_chart_name
10+
11+
def initialize(source_chart_id: , target_dataset_id: , new_chart_name: )
12+
@source_chart_id = source_chart_id
13+
@target_dataset_id = target_dataset_id
14+
@new_chart_name = new_chart_name
15+
end
16+
17+
def perform
18+
raise "Error: source_chart_id integer is required" unless source_chart_id.present? && source_chart_id.is_a?(Integer)
19+
raise "Error: target_dataset_id integer is required" unless target_dataset_id.present? && target_dataset_id.is_a?(Integer)
20+
raise "Error: new_chart_name string is required" unless new_chart_name.present? && new_chart_name.is_a?(String)
21+
22+
logger.info("Duplicating Chart #{source_chart_id}:#{source_chart['slice_name']}. New chart dataset #{target_dataset_id} and new chart name #{new_chart_name}")
23+
Superset::Chart::Create.new(params: new_chart_params).perform
24+
end
25+
26+
private
27+
28+
def new_chart_params
29+
# pulled list from Swagger GUI for chart POST request
30+
# commented out params seem to be not required .. figured out by trial and error
31+
{
32+
#"cache_timeout": 0,
33+
#"certification_details": "string",
34+
#"certified_by": "string",
35+
#"dashboards": [ 0 ],
36+
"datasource_id": target_dataset_id,
37+
# "datasource_name": new_chart_name,
38+
"datasource_type": "table",
39+
# "description": "",
40+
# "external_url": "string",
41+
# "is_managed_externally": true,
42+
# "owners": [ 3 ], # TODO .. check if this is a Required attr, might need to get current API users id.
43+
"params": new_chart_internal_params,
44+
"query_context": new_chart_internal_query_context,
45+
"query_context_generation": true,
46+
"slice_name": new_chart_name,
47+
"viz_type": source_chart['viz_type']
48+
}
49+
end
50+
51+
def new_chart_internal_params
52+
new_params = JSON.parse(source_chart['params'])
53+
new_params['datasource'] = new_params['datasource'].gsub(source_chart_dataset_id.to_s, target_dataset_id.to_s)
54+
new_params.delete('slice_id') # refers to the source chart id .. a new id will be generated in the new chart
55+
new_params.to_json
56+
end
57+
58+
def new_chart_internal_query_context
59+
new_query_context = JSON.parse(source_chart['query_context'])
60+
new_query_context['datasource'] = new_query_context['datasource']['id'] = target_dataset_id
61+
new_query_context['form_data']['datasource'] = new_query_context['form_data']['datasource'].gsub(source_chart_dataset_id.to_s, target_dataset_id.to_s)
62+
new_query_context['form_data'].delete('slice_id')
63+
new_query_context.to_json
64+
end
65+
66+
def source_chart
67+
@source_chart ||= Superset::Chart::Get.new(source_chart_id).result[0]
68+
end
69+
70+
def source_chart_dataset_id
71+
@source_chart_dataset_id ||= JSON.parse(source_chart[:query_context])['datasource']['id']
72+
end
73+
end
74+
end
75+
end

lib/superset/dataset/list.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def filters
3434
end
3535

3636
def list_attributes
37-
['id', 'table_name', 'schema', 'changed_by_name']
37+
['id', 'table_name', 'database', 'schema', 'changed_by_name']
3838
end
3939
end
4040
end

lib/superset/dataset/update_schema.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ def source_dataset
6868

6969
def validate_proposed_changes
7070
logger.info " Validating Dataset ID: #{source_dataset_id} schema update to #{target_schema} on Database: #{target_database_id}"
71-
raise "Error: source_dataset_id integer is required" unless source_dataset_id.present? && source_dataset_id.is_a?(Integer)
72-
raise "Error: target_database_id integer is required" unless target_database_id.present? && target_database_id.is_a?(Integer)
73-
raise "Error: target_schema string is required" unless target_schema.present? && target_schema.is_a?(String)
71+
raise "Error: source_dataset_id integer is required" unless source_dataset_id.present? && source_dataset_id.is_a?(Integer)
72+
raise "Error: target_database_id integer is required" unless target_database_id.present? && target_database_id.is_a?(Integer)
73+
raise "Error: target_schema string is required" unless target_schema.present? && target_schema.is_a?(String)
74+
raise "Error: schema must be set on the source dataset" unless source_dataset['schema'].present? # required for validating sql_query_includes_hard_coded_schema
7475

7576
# confirm the dataset exist? ... no need as the load_source_dataset method will raise an error if the dataset does not exist
7677

spec/superset/chart/create_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
require 'spec_helper'
2+
require 'superset/chart/create'
3+
4+
RSpec.describe Superset::Chart::Create do
5+
subject { described_class.new(params: params ) }
6+
let(:params) { { title: 'a chart' } }
7+
let(:new_chart_id) { 101 }
8+
9+
let(:response) do
10+
{ 'id' => new_chart_id }
11+
end
12+
13+
before do
14+
allow_any_instance_of(described_class).to receive(:response).and_return(response)
15+
allow(ENV).to receive(:[]).with(any_args) { ''}
16+
end
17+
18+
describe '#perform' do
19+
it 'returns id of the new chart' do
20+
expect(subject.perform).to eq(new_chart_id)
21+
end
22+
23+
context 'raises an error if params are not provided' do
24+
let(:params) { nil }
25+
26+
specify do
27+
expect { subject.perform }.to raise_error("Error: params hash is required")
28+
end
29+
end
30+
end
31+
end

spec/superset/dataset/list_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
specify do
5656
expect(subject.rows).to eq(
5757
[
58-
["2", "birth_names", "public", "bob"],
59-
["3", "birth_days", "public", "bob"]
58+
["2", "birth_names", "{\"database_name\"=>\"examples\", \"id\"=>1}", "public", "bob"],
59+
["3", "birth_days", "{\"database_name\"=>\"examples\", \"id\"=>1}", "public", "bob"]
6060
]
6161
)
6262
end

spec/superset/dataset/update_schema_spec.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
remove_copy_suffix: remove_copy_suffix) }
99

1010
let(:source_dataset_id) { 226 }
11+
let(:source_schema) { 'schema_one' }
1112
let(:target_database_id) { 6 }
1213
let(:target_schema) { 'schema_three' }
1314
let(:remove_copy_suffix) { false }
@@ -59,7 +60,7 @@
5960
"normalize_columns"=>false,
6061
"offset"=>0,
6162
"owners"=>[],
62-
"schema"=>"schema_one",
63+
"schema"=>source_schema,
6364
"sql"=>"select count(*),\nservice\n\nfrom blahblah \ngroup by service",
6465
"table_name"=>"JR SP Service Counts (COPY)",
6566
"template_params"=>nil
@@ -115,6 +116,15 @@
115116
expect { subject.perform }.to raise_error(RuntimeError, "Error: Schema schema_four does not exist in database: 6")
116117
end
117118
end
119+
120+
context 'source dataset schema is not configured' do
121+
let(:source_schema) { '' }
122+
123+
specify do
124+
expect { subject.perform }.to raise_error(RuntimeError, "Error: schema must be set on the source dataset")
125+
end
126+
end
127+
118128
end
119129
end
120130

0 commit comments

Comments
 (0)