Skip to content

Commit

Permalink
Merge pull request #34 from rdytech/NEP-18440-add-create-chart-endpoint
Browse files Browse the repository at this point in the history
NEP-18440 add create chart endpoint
  • Loading branch information
jbat authored Sep 2, 2024
2 parents 864b931 + b582d2f commit 5e40a8f
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 7 deletions.
40 changes: 40 additions & 0 deletions lib/superset/chart/create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
=begin
Create a new chart from a set of params
Suggestion is to base your params of an existing charts params and then modify them as needed
So .. why not call the Superset::Chart::Duplicate class which then calls this Chart::Create class
This class is a bit more generic and can be used to create a new chart from scratch (if your confident in the params)
Usage:
Superset::Chart::Create.new(params: new_chart_params).perform
=end

module Superset
module Chart
class Create < Superset::Request

attr_reader :params

def initialize(params: )
@params = params
end

def perform
raise "Error: params hash is required" unless params.present? && params.is_a?(Hash)

logger.info("Creating New Chart")
response['id']
end

def response
@response ||= client.post(route, params)
end

private

def route
"chart/"
end
end
end
end
75 changes: 75 additions & 0 deletions lib/superset/chart/duplicate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# There is no API endpoint to duplicate charts in Superset.
# This class is a workaround.
# Requires a source chart id, target dataset id

module Superset
module Chart
class Duplicate < Superset::Request

attr_reader :source_chart_id, :target_dataset_id, :new_chart_name

def initialize(source_chart_id: , target_dataset_id: , new_chart_name: )
@source_chart_id = source_chart_id
@target_dataset_id = target_dataset_id
@new_chart_name = new_chart_name
end

def perform
raise "Error: source_chart_id integer is required" unless source_chart_id.present? && source_chart_id.is_a?(Integer)
raise "Error: target_dataset_id integer is required" unless target_dataset_id.present? && target_dataset_id.is_a?(Integer)
raise "Error: new_chart_name string is required" unless new_chart_name.present? && new_chart_name.is_a?(String)

logger.info("Duplicating Chart #{source_chart_id}:#{source_chart['slice_name']}. New chart dataset #{target_dataset_id} and new chart name #{new_chart_name}")
Superset::Chart::Create.new(params: new_chart_params).perform
end

private

def new_chart_params
# pulled list from Swagger GUI for chart POST request
# commented out params seem to be not required .. figured out by trial and error
{
#"cache_timeout": 0,
#"certification_details": "string",
#"certified_by": "string",
#"dashboards": [ 0 ],
"datasource_id": target_dataset_id,
# "datasource_name": new_chart_name,
"datasource_type": "table",
# "description": "",
# "external_url": "string",
# "is_managed_externally": true,
# "owners": [ 3 ], # TODO .. check if this is a Required attr, might need to get current API users id.
"params": new_chart_internal_params,
"query_context": new_chart_internal_query_context,
"query_context_generation": true,
"slice_name": new_chart_name,
"viz_type": source_chart['viz_type']
}
end

def new_chart_internal_params
new_params = JSON.parse(source_chart['params'])
new_params['datasource'] = new_params['datasource'].gsub(source_chart_dataset_id.to_s, target_dataset_id.to_s)
new_params.delete('slice_id') # refers to the source chart id .. a new id will be generated in the new chart
new_params.to_json
end

def new_chart_internal_query_context
new_query_context = JSON.parse(source_chart['query_context'])
new_query_context['datasource'] = new_query_context['datasource']['id'] = target_dataset_id
new_query_context['form_data']['datasource'] = new_query_context['form_data']['datasource'].gsub(source_chart_dataset_id.to_s, target_dataset_id.to_s)
new_query_context['form_data'].delete('slice_id')
new_query_context.to_json
end

def source_chart
@source_chart ||= Superset::Chart::Get.new(source_chart_id).result[0]
end

def source_chart_dataset_id
@source_chart_dataset_id ||= JSON.parse(source_chart[:query_context])['datasource']['id']
end
end
end
end
2 changes: 1 addition & 1 deletion lib/superset/dataset/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def filters
end

def list_attributes
['id', 'table_name', 'schema', 'changed_by_name']
['id', 'table_name', 'database', 'schema', 'changed_by_name']
end
end
end
Expand Down
7 changes: 4 additions & 3 deletions lib/superset/dataset/update_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ def source_dataset

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

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

Expand Down
31 changes: 31 additions & 0 deletions spec/superset/chart/create_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'spec_helper'
require 'superset/chart/create'

RSpec.describe Superset::Chart::Create do
subject { described_class.new(params: params ) }
let(:params) { { title: 'a chart' } }
let(:new_chart_id) { 101 }

let(:response) do
{ 'id' => new_chart_id }
end

before do
allow_any_instance_of(described_class).to receive(:response).and_return(response)
allow(ENV).to receive(:[]).with(any_args) { ''}
end

describe '#perform' do
it 'returns id of the new chart' do
expect(subject.perform).to eq(new_chart_id)
end

context 'raises an error if params are not provided' do
let(:params) { nil }

specify do
expect { subject.perform }.to raise_error("Error: params hash is required")
end
end
end
end
4 changes: 2 additions & 2 deletions spec/superset/dataset/list_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
specify do
expect(subject.rows).to eq(
[
["2", "birth_names", "public", "bob"],
["3", "birth_days", "public", "bob"]
["2", "birth_names", "{\"database_name\"=>\"examples\", \"id\"=>1}", "public", "bob"],
["3", "birth_days", "{\"database_name\"=>\"examples\", \"id\"=>1}", "public", "bob"]
]
)
end
Expand Down
12 changes: 11 additions & 1 deletion spec/superset/dataset/update_schema_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
remove_copy_suffix: remove_copy_suffix) }

let(:source_dataset_id) { 226 }
let(:source_schema) { 'schema_one' }
let(:target_database_id) { 6 }
let(:target_schema) { 'schema_three' }
let(:remove_copy_suffix) { false }
Expand Down Expand Up @@ -59,7 +60,7 @@
"normalize_columns"=>false,
"offset"=>0,
"owners"=>[],
"schema"=>"schema_one",
"schema"=>source_schema,
"sql"=>"select count(*),\nservice\n\nfrom blahblah \ngroup by service",
"table_name"=>"JR SP Service Counts (COPY)",
"template_params"=>nil
Expand Down Expand Up @@ -115,6 +116,15 @@
expect { subject.perform }.to raise_error(RuntimeError, "Error: Schema schema_four does not exist in database: 6")
end
end

context 'source dataset schema is not configured' do
let(:source_schema) { '' }

specify do
expect { subject.perform }.to raise_error(RuntimeError, "Error: schema must be set on the source dataset")
end
end

end
end

Expand Down

0 comments on commit 5e40a8f

Please sign in to comment.