cartodb/app/models/carto/visualization_export.rb
2020-06-15 10:58:47 +08:00

115 lines
4.1 KiB
Ruby

require 'uri'
require 'fileutils'
require 'active_record'
require_relative '../../services/carto/visualizations_export_service_2'
require_relative '../../../services/sql-api/sql_api'
require_relative './visualization'
require_dependency 'carto/visualization_exporter'
module Carto
class VisualizationExport < ::ActiveRecord::Base
include VisualizationExporter
include Carto::VisualizationsExportService2Validator
belongs_to :visualization, class_name: Carto::Visualization
belongs_to :user, class_name: Carto::User
belongs_to :log, class_name: Carto::Log
validate :visualization_exportable_by_user?, if: :new_record?
validate :user_tables_ids_valid?, if: :new_record?
validate :valid_visualization_type?, if: :new_record?
STATE_PENDING = 'pending'.freeze
STATE_EXPORTING = 'exporting'.freeze
STATE_UPLOADING = 'uploading'.freeze
STATE_COMPLETE = 'complete'.freeze
STATE_FAILURE = 'failure'.freeze
def exporter_folder
ensure_folder(exporter_config['exporter_temporal_folder'] || DEFAULT_EXPORTER_TMP_FOLDER)
end
def run_export!(file_upload_helper: default_file_upload_helper, download_path: nil)
logger = Carto::Log.new(type: 'visualization_export')
logger.append('Exporting')
update_attributes(state: STATE_EXPORTING, log: logger)
filepath = export(visualization, user, user_tables_ids: user_tables_ids)
logger.append('Uploading')
update_attributes(state: STATE_UPLOADING, file: filepath)
file = CartoDB::FileUploadFile.new(filepath)
s3_config = Cartodb.config[:exporter]['s3'].deep_dup || {}
plain_name = header_encode(file.original_filename.force_encoding('iso-8859-1'))
utf_name = header_encode(file.original_filename)
s3_config['content-disposition'] = %{attachment;filename="#{plain_name}";filename*=utf-8''#{utf_name}}
results = file_upload_helper.upload_file_to_storage(
file_param: file,
s3_config: s3_config,
allow_spaces: true,
force_s3_upload: true
)
if results[:file_uri].present?
logger.append("By file_upload_helper: #{results[:file_uri]}, #{filepath}, (ignored: #{results[:file_path]})")
export_url = results[:file_uri]
export_file = filepath
else
logger.append("Ad-hoc export download: #{results[:file_path]} (ignored: #{filepath})")
export_url = download_path
export_file = results[:file_path]
end
logger.append('Deleting tmp file')
FileUtils.rm(filepath)
state = export_url.present? && export_file.present? ? STATE_COMPLETE : STATE_FAILURE
logger.append("Finishing. State: #{state}. File: #{export_file}. URL: #{url}")
update_attributes(state: state, file: export_file, url: export_url)
true
rescue => e
logger.append_exception('Exporting', exception: e)
CartoDB::Logger.error(
exception: e,
message: "Visualization export error",
user: user,
visualization_id: visualization.id,
visualization_export_id: id)
update_attributes(state: STATE_FAILURE)
false
end
private
def header_encode(name)
URI.encode(name, /[^#{URI::PATTERN::UNRESERVED}]/)
end
def default_file_upload_helper
CartoDB::FileUpload.new(Cartodb.get_config(:exporter, "uploads_path"))
end
def visualization_exportable_by_user?
errors.add(:user, 'Must be able to view the visualization') unless visualization.is_viewable_by_user?(user)
end
def user_tables_ids_valid?
return unless user_tables_ids.present?
related_tables_ids = visualization.related_tables_readable_by(user).map(&:id)
not_valid = user_tables_ids.split(',').select { |user_table_id| !related_tables_ids.include?(user_table_id) }
not_valid.each do |user_table_id|
errors.add(:user_tables_ids, "User table #{user_table_id} is not related to visualization #{visualization.id}")
end
end
def valid_visualization_type?
errors.add(:visualization, 'Only derived visualizations can be exported') unless visualization.derived?
end
end
end