class Sentry::Transport

def envelope_from_event(event)

def envelope_from_event(event)
  # Convert to hash
  event_payload = event.to_hash
  event_id = event_payload[:event_id] || event_payload["event_id"]
  item_type = event_payload[:type] || event_payload["type"]
  envelope_headers = {
    event_id: event_id,
    dsn: @dsn.to_s,
    sdk: Sentry.sdk_meta,
    sent_at: Sentry.utc_now.iso8601
  }
  if event.is_a?(TransactionEvent) && event.dynamic_sampling_context
    envelope_headers[:trace] = event.dynamic_sampling_context
  end
  envelope = Envelope.new(envelope_headers)
  envelope.add_item(
    { type: item_type, content_type: 'application/json' },
    event_payload
  )
  if event.is_a?(TransactionEvent) && event.profile
    envelope.add_item(
      { type: 'profile', content_type: 'application/json' },
      event.profile
    )
  end
  client_report_headers, client_report_payload = fetch_pending_client_report
  envelope.add_item(client_report_headers, client_report_payload) if client_report_headers
  envelope
end

def fetch_pending_client_report

def fetch_pending_client_report
  return nil unless @send_client_reports
  return nil if @last_client_report_sent > Time.now - CLIENT_REPORT_INTERVAL
  return nil if @discarded_events.empty?
  discarded_events_hash = @discarded_events.map do |key, val|
    reason, type = key
    # 'event' has to be mapped to 'error'
    category = type == 'transaction' ? 'transaction' : 'error'
    { reason: reason, category: category, quantity: val }
  end
  item_header = { type: 'client_report' }
  item_payload = {
    timestamp: Sentry.utc_now.iso8601,
    discarded_events: discarded_events_hash
  }
  @discarded_events = Hash.new(0)
  @last_client_report_sent = Time.now
  [item_header, item_payload]
end

def generate_auth_header

def generate_auth_header
  now = Sentry.utc_now.to_i
  fields = {
    'sentry_version' => PROTOCOL_VERSION,
    'sentry_client' => USER_AGENT,
    'sentry_timestamp' => now,
    'sentry_key' => @dsn.public_key
  }
  fields['sentry_secret'] = @dsn.secret_key if @dsn.secret_key
  'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
end

def initialize(configuration)

def initialize(configuration)
  @logger = configuration.logger
  @transport_configuration = configuration.transport
  @dsn = configuration.dsn
  @rate_limits = {}
  @send_client_reports = configuration.send_client_reports
  if @send_client_reports
    @discarded_events = Hash.new(0)
    @last_client_report_sent = Time.now
  end
end

def is_rate_limited?(item_type)

def is_rate_limited?(item_type)
  # check category-specific limit
  category_delay =
    case item_type
    when "transaction"
      @rate_limits["transaction"]
    when "sessions"
      @rate_limits["session"]
    else
      @rate_limits["error"]
    end
  # check universal limit if not category limit
  universal_delay = @rate_limits[nil]
  delay =
    if category_delay && universal_delay
      if category_delay > universal_delay
        category_delay
      else
        universal_delay
      end
    elsif category_delay
      category_delay
    else
      universal_delay
    end
  !!delay && delay > Time.now
end

def record_lost_event(reason, item_type)

def record_lost_event(reason, item_type)
  return unless @send_client_reports
  return unless CLIENT_REPORT_REASONS.include?(reason)
  @discarded_events[[reason, item_type]] += 1
end

def reject_rate_limited_items(envelope)

def reject_rate_limited_items(envelope)
  envelope.items.reject! do |item|
    if is_rate_limited?(item.type)
      log_info("[Transport] Envelope item [#{item.type}] not sent: rate limiting")
      record_lost_event(:ratelimit_backoff, item.type)
      true
    else
      false
    end
  end
end

def send_data(data, options = {})

def send_data(data, options = {})
  raise NotImplementedError
end

def send_envelope(envelope)

def send_envelope(envelope)
  reject_rate_limited_items(envelope)
  return if envelope.items.empty?
  data, serialized_items = serialize_envelope(envelope)
  if data
    log_info("[Transport] Sending envelope with items [#{serialized_items.map(&:type).join(', ')}] #{envelope.event_id} to Sentry")
    send_data(data)
  end
end

def send_event(event)

def send_event(event)
  envelope = envelope_from_event(event)
  send_envelope(envelope)
  event
end

def serialize_envelope(envelope)

def serialize_envelope(envelope)
  serialized_items = []
  serialized_results = []
  envelope.items.each do |item|
    result, oversized = item.serialize
    if oversized
      log_info("Envelope item [#{item.type}] is still oversized after size reduction: {#{item.size_breakdown}}")
      next
    end
    serialized_results << result
    serialized_items << item
  end
  data = [JSON.generate(envelope.headers), *serialized_results].join("\n") unless serialized_results.empty?
  [data, serialized_items]
end