cartodb-4.42/app/controllers/carto/api/analyses_controller.rb
2024-04-06 05:25:13 +00:00

155 lines
5.5 KiB
Ruby

require_dependency 'carto/uuidhelper'
require_relative '../builder/builder_users_module'
module Carto
module Api
class AnalysesController < ::Api::ApplicationController
include Carto::UUIDHelper
include Carto::Builder::BuilderUsersModule
ssl_required :show, :create, :update, :destroy
before_filter :builder_users_only
before_filter :load_visualization
before_filter :check_user_can_add_analysis, only: [:show, :create, :update, :destroy]
before_filter :load_analysis, only: [:show, :update, :destroy]
rescue_from StandardError, with: :rescue_from_standard_error
rescue_from Carto::LoadError, with: :rescue_from_carto_error
rescue_from Carto::UnauthorizedError, with: :rescue_from_carto_error
rescue_from Carto::UnprocesableEntityError, with: :rescue_from_carto_error
def show
render_jsonp(AnalysisPresenter.new(@analysis).to_poro)
end
def create
natural_id = analysis_definition_from_request['id']
analysis = Carto::Analysis.find_by_natural_id(@visualization.id, natural_id)
if analysis
analysis.analysis_definition = analysis_definition_from_request
else
analysis = Carto::Analysis.new(
visualization_id: @visualization.id,
user_id: current_user.id,
analysis_definition: analysis_definition_from_request
)
end
analysis.save!
render_jsonp(AnalysisPresenter.new(analysis).to_poro, 201)
end
def update
new_definition = analysis_definition_from_request
new_root_node = Carto::AnalysisNode.new(new_definition.deep_symbolize_keys)
modified_node_ids = find_modified_nodes(@analysis.analysis_node, new_root_node)
affected_node_ids = find_affected_nodes(modified_node_ids)
@analysis.analysis_definition = new_definition
@analysis.save!
purge_layer_node_style_history(affected_node_ids)
render_jsonp(AnalysisPresenter.new(@analysis).to_poro, 200)
end
def destroy
@analysis.destroy
render_jsonp(AnalysisPresenter.new(@analysis).to_poro, 200)
end
private
def purge_layer_node_style_history(node_ids)
Carto::LayerNodeStyle.from_visualization_and_source(@visualization, node_ids).delete_all
end
def find_affected_nodes(modified_node_ids)
all_visualization_nodes = @visualization.analyses.map(&:analysis_node).map(&:descendants).flatten
all_visualization_nodes.select { |node|
node.descendants.any? { |descendant| modified_node_ids.include?(descendant.id) }
}.map(&:id)
end
def find_modified_nodes(old_root, new_root)
old_nodes = old_root.descendants
new_nodes = new_root.descendants
old_ids = old_nodes.map(&:id)
new_ids = new_nodes.map(&:id)
kept_ids = old_ids & new_ids
kept_ids.select do |node_id|
old_node = old_nodes.find { |n| n.id == node_id }
new_node = new_nodes.find { |n| n.id == node_id }
old_node.non_child_params != new_node.non_child_params
end
end
def analysis_definition_from_request
analysis_json = json_post(request.raw_post)
if analysis_json.nil? || analysis_json.empty?
raise Carto::UnprocesableEntityError.new("Empty analysis")
end
analysis_definition = analysis_json['analysis_definition']
if analysis_definition.nil? || analysis_definition.empty? || analysis_definition.class == String
raise Carto::UnprocesableEntityError.new("Invalid analysis definition")
end
analysis_definition
end
def json_post(raw_post = request.raw_post)
@json_post ||= (raw_post.present? ? JSON.parse(raw_post) : nil)
rescue StandardError => e
# Malformed JSON is not our business
CartoDB.notify_warning_exception(e)
raise UnprocesableEntityError.new("Malformed JSON: #{raw_post}")
end
def load_visualization
visualization_id = params[:visualization_id]
if payload_visualization_id.present? && payload_visualization_id != visualization_id
raise UnprocesableEntityError.new("url vis (#{visualization_id}) != payload (#{payload_visualization_id})")
end
@visualization = Carto::Visualization.where(id: visualization_id).first if visualization_id
raise LoadError.new("Visualization not found: #{visualization_id}") unless @visualization
end
def payload_visualization_id
json_post.present? ? json_post['visualization_id'] : nil
end
def payload_analysis_id
json_post.present? ? json_post['id'] : nil
end
def check_user_can_add_analysis
if @visualization.user_id != current_user.id
raise Carto::UnauthorizedError.new("#{current_user.id} doesn't own visualization #{@visualization.id}")
end
end
def load_analysis
if payload_analysis_id.present? && payload_analysis_id != params[:id]
raise UnprocesableEntityError.new("url analysis (#{params[:id]}) != payload (#{payload_analysis_id})")
end
unless params[:id].nil?
@analysis = Carto::Analysis.where(id: params[:id]).first if uuid?(params[:id])
if @analysis.nil?
@analysis = Carto::Analysis.find_by_natural_id(@visualization.id, params[:id])
end
end
raise Carto::LoadError.new("Analysis not found: #{params[:id]}") unless @analysis
end
end
end
end