class Eco::API::UseCases::GraphQL::Samples::Location # Logic to: # 1. Track-down results and errors # 2. Create tags remap csv table batch design module Command::Results include Eco::API::UseCases::GraphQL::Helpers::Location::Base attr_accessor :error, :exception attr_accessor :tags_remap_csv_file def rescued yield rescue StandardError => e log(:error) { self.exception ||= e.patch_full_message } end def request_results_class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Results end # Capture results def results @results ||= {} end # The maps of tags to be used in batch remap tags # @return [Array] source/destination pairs of `Array` def tags_remap_table @tags_remap_table ||= [] end # Errors tracking/logging. # @note it gives feedback on where an error has occurred. # @param page_results [Eco::API::UseCases::GraphQL::Helpers::Locations::Commands::CommandResults] # @param stage [Symbol] used when we launch an update in different phases (i.e. rename, move, etc.) # @return [Boolean] whether or not there was an error def page_errors?(page_results, page, pages, done, total, stage: nil) raise "Expecting CommandResults object. Given: #{page_results.class}" unless page_results.is_a?(request_results_class) stage_str = stage ? "'#{stage}' " : '' fingerprint = "#{stage_str}#{page} (of #{pages})" errored = false if page_results.error? errored = true log(:error) { "Error on #{fingerprint}: #{page_results.error.doc.pretty_inspect}" } end if page_results.applied? log(:info) { "Success on #{fingerprint}: #{done} (of #{total}) commands applied!" } elsif page_results.errored? errored = true msg = "Some command failed on #{fingerprint}:\n#{page_results.stats}" unless force_continue? first_errored = page_results.first_errored msg << "The error(s) - #{first_errored.error_msg}\n" end log(:error) { msg } end errored end # Based on commands that succeded, and the batch stage, it tracks # the tag remaps that should be batches against existing pages # @note # 1. This requires to have available the `current_tree` locations structure # - Fortunatelly this is being tracked, as it is returned as payload of the response. # 2. Based on the assumption that the order of the commands (stages) happens like this: # - :unarchive, :id_name, :insert, :move, :archive # 3. The only update operations that generate tag remaps are `:id` (or `:id_name`) and `:move`. def update_tags_remap_table(results, stage) return false if [:unarchive, :archive].include?(stage) raise "Expecting CommandResults object. Given: #{results.class}" unless results.is_a?(request_results_class) results.applied.each do |result| case stage when :id, :id_name prev_id, curr_id = result.command_input_data.values_at(:nodeId, :newId) unless current_tree.tag?(curr_id) msg = "Node '#{prev_id}' was updated to '#{curr_id}', " msg << "but in current structure '#{curr_id}' is not present" log(:warn) { msg } end tags_remap_table << [[prev_id], [curr_id]] when :move node_id, parent_id = result.command_input_data.values_at(:nodeId, :parentId) prev_node = previous_tree.node(node_id) curr_node = current_tree.node(node_id) lost_tags = prev_node.path - curr_node.path new_tags = curr_node.path - prev_node.path curr_parent = curr_node.parent.top? ? nil : curr_node.parent unless curr_parent&.id == parent_id msg = "Node '#{node_id}' was moved uner '#{parent_id}', " msg << "but in current structure has parent '#{curr_parent&.id}'" log(:warn) { msg } end tags_remap_table << [lost_tags.unshift(node_id), new_tags.unshift(node_id)] end end end # Generates the final tags remap file def generate_tags_remap_csv(filename = "cache/remap_tags.csv") return nil if tags_remap_table.empty? timestamp_file(filename).tap do |file| CSV.open(file, 'w') do |csv| csv << ["source_tags", "destination_tags"] tags_remap_table.each do |(src_tags, dst_tags)| csv << [src_tags.join('|'), dst_tags.join('|')] end end log(:info) { "Generated file '#{file}'" } end end # Makes the file relative to the enviro def timestamp_file(filename, enviro_relative: true) filename = session.file_manager.dir.file(filename) if enviro_relative Eco::Data::Files.timestamp_file(filename) end end end