|
| 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 |
0 commit comments