From b7b41c3150a5e0e3281812bb0e5ba9ed0362c3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mart=C3=ADn?= Date: Fri, 15 Mar 2019 11:35:47 +0100 Subject: [PATCH] removing old visualization_export_service --- Makefile | 1 - .../carto/visualizations_export_service.rb | 264 ------------------ .../visualizations_export_service_spec.rb | 219 --------------- 3 files changed, 484 deletions(-) delete mode 100644 app/services/carto/visualizations_export_service.rb delete mode 100644 spec/services/carto/visualizations_export_service_spec.rb diff --git a/Makefile b/Makefile index 5d5fa1ffaa..1a4d93e99b 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,6 @@ WORKING_SPECS_1 = \ spec/services/carto/data_library_service_spec.rb \ spec/services/carto/user_authenticator_spec.rb \ spec/requests/sessions_controller_spec.rb \ - spec/services/carto/visualizations_export_service_spec.rb \ spec/services/carto/visualizations_export_service_2_spec.rb \ $(NULL) diff --git a/app/services/carto/visualizations_export_service.rb b/app/services/carto/visualizations_export_service.rb deleted file mode 100644 index 07eb08f379..0000000000 --- a/app/services/carto/visualizations_export_service.rb +++ /dev/null @@ -1,264 +0,0 @@ -# encoding: utf-8 - -require 'cgi' -require 'date' - -require_relative "../../controllers/carto/api/visualization_vizjson_adapter" - -module Carto - class VisualizationsExportService - - FEATURE_FLAG_NAME = "visualizations_backup" - - DAYS_TO_KEEP_BACKUP = 15 - - SERVICE_VERSION = 1 - - def purge_old - items = retrieve_old_backups - items.each do |item| - remove_backup(item) - end - items.length - end - - def export(visualization_id) - visualization = Carto::Visualization.where(id: visualization_id).first - raise "Visualization with id #{visualization_id} not found" unless visualization - - data = export_to_json(visualization) - - backup_present = Carto::VisualizationBackup.where( - username: visualization.user.username, - visualization: visualization.id).first != nil - - if backup_present - false - else - backup_entry = Carto::VisualizationBackup.new( - username: visualization.user.username, - visualization: visualization.id, - export_vizjson: data - ) - backup_entry.save - - true - end - rescue VisualizationsExportServiceError => export_error - raise export_error - rescue => exception - raise VisualizationsExportServiceError.new("Export error: #{exception.message} #{exception.backtrace}") - end - - def export_to_json(visualization) - vizjson_options = { - full: true, - user_name: visualization.user.username, - user_api_key: visualization.user.api_key, - user: visualization.user, - viewer_user: visualization.user - } - - CartoDB::Visualization::VizJSON.new( - Carto::Api::VisualizationVizJSONAdapter.new(visualization, $tables_metadata), vizjson_options, Cartodb.config) - .to_export_poro(export_version) - .to_json - end - - def import(visualization_id, skip_version_check = false) - restore_result = restore_backup(visualization_id, skip_version_check) - remove_backup(visualization_id) if restore_result - true - rescue VisualizationsExportServiceError => export_error - raise export_error - rescue => exception - raise VisualizationsExportServiceError.new("Import error: #{exception.message} #{exception.backtrace}") - end - - def restore_from_json(dump_data) - user = ::User.where(id: dump_data["owner"]["id"]).first - - base_layer = create_base_layer(user, dump_data) - - map = create_map(user, base_layer) - - add_data_layers(map, dump_data) - - add_labels_layer(map, base_layer, dump_data) - - set_map_data(map, dump_data) - - description = dump_data["description"] - - default_privacy = Carto::Visualization::PRIVACY_LINK - privacy = user.valid_privacy?(default_privacy) ? default_privacy : Carto::Visualization::PRIVACY_PUBLIC - visualization = create_visualization( - id: dump_data["id"], - name: dump_data["title"], - description: (description.nil? || description.empty?) ? "" : CGI.unescapeHTML(description), - type: Carto::Visualization::TYPE_DERIVED, - privacy: privacy, - user_id: user.id, - map_id: map.id, - kind: Carto::Visualization::KIND_GEOM - ) - - add_overlays(visualization, dump_data) - - true - end - - private - - # Mainly intended for testing - def export_version - SERVICE_VERSION - end - - def retrieve_old_backups - max_date = Date.today - DAYS_TO_KEEP_BACKUP - Carto::VisualizationBackup.where("created_at <= ?", max_date).pluck(:visualization) - end - - def remove_backup(visualization_id) - backup_item = Carto::VisualizationBackup.where(visualization: visualization_id).first - if backup_item - backup_item.destroy - true - else - false - end - end - - def restore_backup(visualization_id, skip_version_check) - # TODO: support partial restores - visualization = Carto::Visualization.where(id: visualization_id).first - if visualization - raise VisualizationsExportServiceError.new("Visualization with id #{visualization_id} already exists!") - end - - dump_data = get_restore_data(visualization_id, skip_version_check) - - restore_from_json(dump_data) - end - - def get_restore_data(visualization_id, skip_version_check) - restore_data = Carto::VisualizationBackup.where(visualization: visualization_id).first - unless restore_data - raise VisualizationsExportServiceError.new("Restore data not found for visualization id #{visualization_id}") - end - data = ::JSON.parse(restore_data.export_vizjson) - - if data["export_version"] != export_version && !skip_version_check - raise VisualizationsExportServiceError.new( - "Stored data has different version (#{data['export_version']}) than Service (#{export_version})") - end - - data - end - - def add_overlays(visualization, exported_data) - exported_data["overlays"].each do |exported_overlay| - Carto::Overlay.new(exported_overlay.merge('visualization_id' => visualization.id)).save - end - - true - end - - def set_map_data(map, exported_data) - map.recalculate_bounds! - - map.scrollwheel = exported_data["scrollwheel"] if exported_data["scrollwheel"] - map.legends = exported_data["legends"] if exported_data["legends"] - if exported_data["bounds"] && exported_data["bounds"].length == 2 - map.view_bounds_sw = exported_data["bounds"][0].to_s - map.view_bounds_ne = exported_data["bounds"][1].to_s - end - map.center = exported_data["center"] if exported_data["center"] - map.zoom = exported_data["zoom"] if exported_data["zoom"] - map.provider = exported_data["map_provider"] if exported_data["map_provider"] - - map.save - .reload - end - - def prepare_layer_data(exported_layer) - data = exported_layer.except('id', 'type', 'legend', 'visible') - data['kind'] = layer_kind_from_type(exported_layer['type']) - data - end - - def layer_kind_from_type(exported_layer_type) - if exported_layer_type == 'CartoDB' - 'carto' - else - exported_layer_type.downcase - end - end - - def create_base_layer(user, exported_data) - # Basemap/base layer is always the first layer - layer_data = exported_data["layers"].select { |layer| ::Layer::BASE_LAYER_KINDS.include?(layer["type"]) }.first - if layer_data.nil? - ::ModelFactories::LayerFactory.get_default_base_layer(user) - else - ::ModelFactories::LayerFactory.get_new(prepare_layer_data(layer_data)) - end - end - - def add_data_layer(map, layer_data) - data_layer = ::ModelFactories::LayerFactory.get_new(prepare_layer_data(layer_data)) - map.add_layer(data_layer) - data_layer - end - - def add_labels_layer(map, base_layer, exported_data) - return unless base_layer.supports_labels_layer? - - base_layers = exported_data["layers"].select { |layer| ::Layer::BASE_LAYER_KINDS.include?(layer["type"]) } - - # Remember, basemap layer is 1st one... - if base_layers.count < 2 - # Missing labels layer, regenerate it - add_default_labels_layer(map, base_layer) - else - # ...And labels layer is always last one - labels_layer = ::ModelFactories::LayerFactory.get_new(prepare_layer_data(base_layers.last)) - map.add_layer(labels_layer) - labels_layer - end - end - - def create_map(user, base_layer) - map = ::ModelFactories::MapFactory.get_map(base_layer, user.id) - map.add_layer(base_layer) - map - end - - def add_data_layers(map, exported_data) - data_layers = exported_data["layers"].select do |layer| - kind = layer_kind_from_type(layer["type"]) - ::Layer::DATA_LAYER_KINDS.include?(kind) - end - data_layers.each do |layer| - add_data_layer(map, layer) - end - end - - def create_visualization(attributes) - visualization = Carto::Visualization.new(attributes) - visualization.id = attributes[:id] - visualization.save! - visualization - end - - def add_default_labels_layer(map, base_layer) - labels_layer = ::ModelFactories::LayerFactory.get_default_labels_layer(base_layer) - map.add_layer(labels_layer) - labels_layer - end - - end - - class VisualizationsExportServiceError < StandardError; end -end diff --git a/spec/services/carto/visualizations_export_service_spec.rb b/spec/services/carto/visualizations_export_service_spec.rb deleted file mode 100644 index a112caec46..0000000000 --- a/spec/services/carto/visualizations_export_service_spec.rb +++ /dev/null @@ -1,219 +0,0 @@ -# encoding: utf-8 - -require_relative '../../spec_helper' -require 'helpers/unique_names_helper' -require 'visualization/vizjson' - -describe Carto::VisualizationsExportService do - include UniqueNamesHelper - before(:all) do - @user = FactoryGirl.create(:valid_user, private_tables_enabled: true) - end - - after(:all) do - @user.destroy - end - - before(:each) do - bypass_named_maps - ::User.any_instance - .stubs(:has_feature_flag?) - .returns(false) - ::User.any_instance - .stubs(:has_feature_flag?) - .with(Carto::VisualizationsExportService::FEATURE_FLAG_NAME) - .returns(true) - end - - after(:each) do - Carto::VisualizationBackup.delete_all - end - - it "Calls data export upon visualization deletion" do - visualization = create_vis(@user) - - Carto::VisualizationsExportService.any_instance - .expects(:export) - .with(visualization.id) - .returns(true) - - visualization.delete - end - - it "Exports data to DB" do - visualization = create_vis(@user) - - visualization_clone = visualization.dup - - visualization.delete - - backup = Carto::VisualizationBackup.where(visualization: visualization_clone.id).first - backup.should_not eq nil - backup.visualization.should eq visualization_clone.id - backup.username.should eq visualization_clone.user.username - backup.export_vizjson.should_not eq nil - backup.export_vizjson.should_not eq "" - end - - it "Purges old backup entries when told to do so" do - visualization = create_vis(@user) - visualization.delete - visualization = create_vis(@user) - visualization.delete - visualization = create_vis(@user) - visualization.delete - - old_date = Date.today - (Carto::VisualizationsExportService::DAYS_TO_KEEP_BACKUP * 2).days - Carto::VisualizationBackup.update_all "created_at='#{old_date}'" - - purged_items = Carto::VisualizationsExportService.new.purge_old - - purged_items.should eq 3 - Carto::VisualizationBackup.where(username: @user.username).count.should eq 0 - end - - it "Deletes backup after successfully restoring" do - visualization = create_vis(@user) - - visualization_clone = visualization.dup - - visualization.delete - - result = Carto::VisualizationsExportService.new.import(visualization_clone.id) - result.should eq true - - expect { - Carto::VisualizationsExportService.new.import(visualization_clone.id) - }.to raise_error Carto::VisualizationsExportServiceError - - Carto::VisualizationBackup.where(visualization: visualization_clone.id).count.should eq 0 - end - - it "Imports data from DB" do - table_1 = create_table(user_id: @user.id) - table_2 = create_table(user_id: @user.id) - - blender = Visualization::TableBlender.new(Carto::User.find(@user.id), [table_1, table_2]) - map = blender.blend - - visualization = create_vis(@user, map_id: map.id, description: 'description with tags') - - # Keep data for later comparisons - base_layer = visualization.layers(:base).first - visualization_clone = visualization.dup - - original_data_layer_names = visualization.layers(:data).map { |layer| layer.options["table_name"] } - - # As duplicating the vis only works fine with parent object, store also the vizjson for comparisons - vizjson_options = { - full: true, - user_name: visualization.user.username, - user_api_key: visualization.user.api_key, - user: visualization.user, - viewer_user: visualization.user - } - original_vizjson = CartoDB::Visualization::VizJSON.new( - Carto::Api::VisualizationVizJSONAdapter.new(visualization, $tables_metadata), vizjson_options, Cartodb.config) - .to_poro - .to_json - original_vizjson = ::JSON.parse(original_vizjson) - - visualization.delete - - Carto::VisualizationsExportService.new.import(visualization_clone.id) - - # Restore maintains same visualization UUID - restored_visualization = CartoDB::Visualization::Member.new(id: visualization_clone.id).fetch - restored_visualization.nil?.should eq false - - # Can reuse same vizjson options - restored_vizjson = CartoDB::Visualization::VizJSON.new( - Carto::Api::VisualizationVizJSONAdapter.new(restored_visualization, $tables_metadata), - vizjson_options, Cartodb.config) - .to_poro - .to_json - restored_vizjson = ::JSON.parse(restored_vizjson) - - restored_data_layer_names = visualization.layers(:data).map { |layer| layer.options["table_name"] } - - # Base attributes checks - restored_visualization.name.should eq visualization_clone.name - restored_visualization.description.should eq visualization_clone.description - restored_visualization.privacy.should eq CartoDB::Visualization::Member::PRIVACY_LINK - # Vizjson checks - restored_vizjson['map_provider'].should eq original_vizjson['map_provider'] - restored_vizjson['bounds'].should eq original_vizjson['bounds'] - restored_vizjson['center'].should eq original_vizjson['center'] - restored_vizjson['zoom'].should eq original_vizjson['zoom'] - restored_vizjson['overlays'].should eq original_vizjson['overlays'] - - restored_layer_ids = restored_vizjson["layers"].map { |l| l['id'] } - original_layer_ids = original_vizjson["layers"].map { |l| l['id'] } - - # Restoring doesn't keep layer ids (restored layers are stored in the same table) - restored_layer_ids.count.should == original_layer_ids.count - restored_layer_ids.compact.sort.should_not == original_layer_ids.compact.sort - - restored_named_map = restored_vizjson["layers"][1]["options"]["named_map"] - original_named_map = original_vizjson["layers"][1]["options"]["named_map"] - restored_named_map_layer_ids = restored_named_map['layers'].map { |l| l['id'] } - original_named_map_layer_ids = original_named_map['layers'].map { |l| l['id'] } - # Restoring doesn't keep layer ids (restored layers are stored in the same table) - restored_named_map_layer_ids.count.should == original_named_map_layer_ids.count - restored_named_map_layer_ids.compact.sort.should_not == original_named_map_layer_ids.compact.sort - - - # Clear layer named map layers ids - restored_named_map["layers"].each { |l| l['id'] = nil } - original_named_map["layers"].map { |l| l['id'] = nil } - (restored_named_map["layers"] - - original_named_map["layers"]).should eq [] - - # Layer checks - (restored_visualization.layers(:base).count > 0).should eq true - restored_visualization.layers(:base).first["options"].should eq base_layer["options"] - restored_visualization.layers(:data).count.should eq 2 - (restored_data_layer_names - original_data_layer_names).should eq [] - end - - it "Doesn't imports when versioning changes except if forced" do - stubbed_version = -1 - Carto::VisualizationsExportService.any_instance.stubs(:export_version).returns(stubbed_version) - - visualization = create_vis(@user) - visualization_id = visualization.id - visualization.delete - - Carto::VisualizationsExportService.any_instance.unstub(:export_version) - - export_service = Carto::VisualizationsExportService.new - - version = export_service.send (:export_version) - - expect { - export_service.import(visualization_id) - }.to raise_exception Carto::VisualizationsExportServiceError, - "Stored data has different version (#{stubbed_version}) than Service (#{version})" - - result = export_service.import(visualization_id, true) - result.should eq true - end - - private - - def create_vis(user, attributes = {}) - attrs = { - user_id: user.id, - name: attributes.fetch(:name, unique_name('viz')), - map_id: attributes.fetch(:map_id, ::Map.create(user_id: user.id).id), - description: attributes.fetch(:description, 'bogus'), - type: attributes.fetch(:type, 'derived'), - privacy: attributes.fetch(:privacy, 'public') - } - - vis = CartoDB::Visualization::Member.new(attrs) - vis.store - - vis - end -end