require 'json' require 'faraday' module AppMap module Command UploadResponse = Struct.new(:batch_id, :scenario_uuid) UploadStruct = Struct.new(:config, :data, :url, :user, :org) class Upload < UploadStruct MAX_DEPTH = 12 attr_accessor :batch_id def initialize(config, data, url, user, org) super # TODO: Make this an option @max_depth = MAX_DEPTH end def perform appmap = data.clone events = data.delete('events') class_map = data.delete('classMap') || [] unless events.blank? pruned_events = [] stack = [] events.each do |evt| if evt['event'] == 'call' stack << evt stack_depth = stack.length else stack_depth = stack.length stack.pop end prune = stack_depth > @max_depth pruned_events << evt unless prune end warn "Pruned events to #{pruned_events.length}" if events.length > pruned_events.length appmap[:events] = pruned_events appmap[:classMap] = prune(class_map, events: pruned_events) else appmap[:events] = [] appmap[:classMap] = prune(class_map) end upload_file = { user: user, org: org, data: appmap }.compact conn = Faraday.new(url: url) response = conn.post do |req| req.url '/api/scenarios' req.headers['Content-Type'] = 'application/json' req.headers[AppMap::BATCH_HEADER_NAME] = @batch_id if @batch_id req.body = JSON.generate(upload_file) end unless response.body.blank? message = begin JSON.parse(response.body) rescue JSON::ParserError => e warn "Response is not valid JSON (#{e.message})" nil end end unless response.success? error = [ 'Upload failed' ] error << ": #{message}" if message raise error.join end batch_id = @batch_id || response.headers[AppMap::BATCH_HEADER_NAME] uuid = message['uuid'] UploadResponse.new(batch_id, uuid) end protected def debug? ENV['DEBUG'] == 'true' || ENV['GLI_DEBUG'] == 'true' end def prune(class_map, events: nil) require 'appmap/algorithm/prune_class_map' Algorithm::PruneClassMap.new(class_map).tap do |alg| alg.events = events if events alg.logger = ->(msg) { warn msg } if debug? end.perform end end end end