From 1a358e3d29c8321fb6bf55d8f665fa6b065bc54b Mon Sep 17 00:00:00 2001 From: Kartones Date: Mon, 28 Sep 2015 16:38:51 +0200 Subject: [PATCH] #5710 created export service + plugged automatic backup before deletion --- .../backup.rb => visualization_backup.rb} | 4 +- app/models/visualization/member.rb | 9 +- .../carto/visualizations_export_service.rb | 93 +++++++++++++++++++ lib/tasks/viz_maintenance.rake | 83 ++--------------- 4 files changed, 110 insertions(+), 79 deletions(-) rename app/models/carto/{visualization/backup.rb => visualization_backup.rb} (81%) create mode 100644 app/services/carto/visualizations_export_service.rb diff --git a/app/models/carto/visualization/backup.rb b/app/models/carto/visualization_backup.rb similarity index 81% rename from app/models/carto/visualization/backup.rb rename to app/models/carto/visualization_backup.rb index a70f1b58e857..750550f90425 100644 --- a/app/models/carto/visualization/backup.rb +++ b/app/models/carto/visualization_backup.rb @@ -3,8 +3,7 @@ require 'active_record' module Carto - module Visualization - class Backup < ActiveRecord::Base + class VisualizationBackup < ActiveRecord::Base # @param String username # @param Uuid visualization @@ -13,6 +12,5 @@ class Backup < ActiveRecord::Base validates :username, :visualization, :export_vizjson, presence: true - end end end diff --git a/app/models/visualization/member.rb b/app/models/visualization/member.rb index 31a1eab60d89..e3981acc695e 100644 --- a/app/models/visualization/member.rb +++ b/app/models/visualization/member.rb @@ -249,7 +249,14 @@ def fetch self end - def delete(from_table_deletion=false) + def delete(from_table_deletion = false) + begin + Carto::VisualizationsExportService.new.export(id) + rescue => exception + # Don't break deletion flow + CartoDB.notify_exception(exception, user: user, visualization_id: id) + end + # Named map must be deleted before the map, or we lose the reference to it begin named_map = get_named_map diff --git a/app/services/carto/visualizations_export_service.rb b/app/services/carto/visualizations_export_service.rb new file mode 100644 index 000000000000..24409b6eeaa0 --- /dev/null +++ b/app/services/carto/visualizations_export_service.rb @@ -0,0 +1,93 @@ +# encoding: utf-8 + +require_relative "../../controllers/carto/api/visualization_vizjson_adapter" + +module Carto + class VisualizationsExportService + + SERVICE_VERSION = 1 + + + def export(visualization_id) + visualization = Carto::Visualization.where(id: visualization_id).first + raise "Visualization with id #{visualization_id} not found" unless visualization + + vizjson_options = { + full: true, + user_name: visualization.user.username, + user_api_key: visualization.user.api_key, + user: visualization.user, + viewer_user: visualization.user, + export: true + } + + data = CartoDB::Visualization::VizJSON.new( + Carto::Api::VisualizationVizJSONAdapter.new(visualization, $tables_metadata), vizjson_options, Cartodb.config) + .to_export_poro(SERVICE_VERSION) + .to_json + + backup_entry = Carto::VisualizationBackup.new( + username: visualization.user.username, + visualization: visualization.id, + export_vizjson: data + ) + + backup_entry.save + + true + end + + def import(visualization_id) + # TODO: support partial restores + visualization = Carto::Visualization.where(id: visualization_id).first + raise "Visualization with id #{visualization_id} already exists!" if visualization + + restore_data = Carto::VisualizationBackup.where(visualization: visualization_id).first + raise "Restore data not found for visualization id #{visualization_id}" unless restore_data + + dump_data = ::JSON.parse(restore_data.export_vizjson) + + user = ::User.where(id: dump_data["owner"]["id"]).first + + # TODO: Import base layer instead of using default one if present + base_layer = CartoDB::Factories::LayerFactory.get_default_base_layer(user) + map = CartoDB::Factories::MapFactory.get_map(base_layer, user.id) + map.add_layer(base_layer) + + dump_data["layers"].select { |layer| layer["type"] == "layergroup" }.each do |layergroup| + layergroup["options"]["layer_definition"]["layers"].each do |layer| + # TODO: new factory method to "get_data_layer" + data_layer = CartoDB::Factories::LayerFactory.get_default_data_layer(layer["options"]["table_name"], user) + map.add_layer(data_layer) + end + end + + dump_data["layers"].select { |layer| ::Layer::DATA_LAYER_KINDS.include?(layer["type"]) }.each do |layer| + # TODO: new factory method to "get_data_layer" + data_layer = CartoDB::Factories::LayerFactory.get_default_data_layer(layer["options"]["table_name"], user) + map.add_layer(data_layer) + end + + # TODO: Import labels layer instead of using default one if present + if base_layer.supports_labels_layer? + labels_layer = CartoDB::Factories::LayerFactory.get_default_labels_layer(base_layer) + map.add_layer(labels_layer) + end + + visualization = CartoDB::Visualization::Member.new( + id: dump_data["id"], + name: dump_data["title"], + description: dump_data["description"], + type: CartoDB::Visualization::Member::TYPE_DERIVED, + privacy: CartoDB::Visualization::Member::PRIVACY_LINK, + user_id: dump_data["owner"]["id"], + map_id: map.id, + kind: CartoDB::Visualization::Member::KIND_GEOM + ) + + visualization.store + + true + end + end +end diff --git a/lib/tasks/viz_maintenance.rake b/lib/tasks/viz_maintenance.rake index e3b521d1135b..898e5ba4f952 100644 --- a/lib/tasks/viz_maintenance.rake +++ b/lib/tasks/viz_maintenance.rake @@ -66,91 +66,24 @@ namespace :cartodb do desc "Exports/Backups a visualization" task :export_user_visualization, [:vis_id] => :environment do |_, args| - require_relative "../../app/controllers/carto/api/visualization_vizjson_adapter" - - visualization = Carto::Visualization.where(id: args[:vis_id]).first - raise "Visualization with id #{args[:vis_id]} not found" unless visualization - - vizjson_options = { - full: true, - user_name: visualization.user.username, - user_api_key: visualization.user.api_key, - user: visualization.user, - viewer_user: visualization.user, - export: true - } + vis_export_service = Carto::VisualizationsExportService.new puts "Exporting visualization #{args[:vis_id]}..." - data = CartoDB::Visualization::VizJSON.new( - Carto::Api::VisualizationVizJSONAdapter.new(visualization, $tables_metadata), vizjson_options, Cartodb.config) - .to_export_poro(1) - .to_json - - backup_entry = Carto::Visualization::Backup.new( - username: visualization.user.username, - visualization: visualization.id, - export_vizjson: ::JSON.dump(data) - ) - - backup_entry.save + vis_export_service.export(args[:vis_id]) puts "Export complete" end - desc "Imports a visualization using a metadata file" - task :import_user_visualization, [:export_file] => :environment do |_, args| - raise "Export '#{args[:export_file]}' not found" unless File.file?(args[:export_file]) - - puts "Importing visualization data from #{args[:export_file]}" - - dump_data = ::JSON.parse(IO.read(args[:export_file])) - - # TODO: support partial restores - unless Carto::Visualization.where(id: dump_data["id"]).first.nil? - raise "Visualization #{dump_data['id']} already exists" - end - - user = ::User.where(id: dump_data["owner"]["id"]).first - - # TODO: Import base layer instead of using default one if present - base_layer = CartoDB::Factories::LayerFactory.get_default_base_layer(user) - map = CartoDB::Factories::MapFactory.get_map(base_layer, user.id) - map.add_layer(base_layer) - - dump_data["layers"].select { |layer| layer["type"] == "layergroup" }.each do |layergroup| - layergroup["options"]["layer_definition"]["layers"].each do |layer| - # TODO: new factory method to "get_data_layer" - data_layer = CartoDB::Factories::LayerFactory.get_default_data_layer(layer["options"]["table_name"], user) - map.add_layer(data_layer) - end - end - - dump_data["layers"].select { |layer| ::Layer::DATA_LAYER_KINDS.include?(layer["type"]) }.each do |layer| - # TODO: new factory method to "get_data_layer" - data_layer = CartoDB::Factories::LayerFactory.get_default_data_layer(layer["options"]["table_name"], user) - map.add_layer(data_layer) - end - - # TODO: Import labels layer instead of using default one if present - if base_layer.supports_labels_layer? - labels_layer = CartoDB::Factories::LayerFactory.get_default_labels_layer(base_layer) - map.add_layer(labels_layer) - end + desc "Imports/Restores a visualization" + task :import_user_visualization, [:vis_id] => :environment do |_, args| + vis_export_service = Carto::VisualizationsExportService.new - visualization = CartoDB::Visualization::Member.new( - id: dump_data["id"], - name: dump_data["title"], - description: dump_data["description"], - type: CartoDB::Visualization::Member::TYPE_DERIVED, - privacy: CartoDB::Visualization::Member::PRIVACY_LINK, - user_id: dump_data["owner"]["id"], - map_id: map.id, - kind: CartoDB::Visualization::Member::KIND_GEOM) + puts "Importing visualization data for uuid #{args[:vis_id]}" - visualization.store + vis_export_service.import(args[:vis_id]) - puts "Visualization #{visualization.id} imported" + puts "Visualization #{args[:vis_id]} imported" end private