require 'sinatra/base' require 'haml' require 'json' module Batsd::Dash class App < Sinatra::Base configure(:development) { disable :show_exceptions } configure do helpers ParamsHelper, GraphHelper set :haml, format: :html5 set :raise_errors, false config = Batsd::Dash::config.dup || {} set :views, [views, config.delete(:view_path)].compact set :host, config.delete(:host) || 'localhost' set :port, config.delete(:port) || 8127 set :view_names, [:root, :view, :missing, :layout, :loading] @connection_pool = ConnectionPool.new(config) do Connection.new(host, port) end end helpers do def find_template(views, name, engine, &block) path = settings.view_names.include?(name) ? views.first : views.last super path, name, engine, &block end def render_error(msg) render_json 400, error: msg end def render_json(code = 200, json) halt code, String === json ? json : JSON.dump(json) end def connection_pool @connection_pool ||= self.class.instance_variable_get(:@connection_pool) end end error Connection::SocketError do render_json error: env['sinatra.error'].message end get "/", provides: :html do haml :root end get "/graph", provides: :html do haml :view end get %r[/([A-Za-z0-9-_]+)$], provides: :html do begin haml params[:captures].first.to_sym, locals: { user_template: true } rescue Errno::ENOENT haml :missing end end get "/version", provides: :json do render_json version: BatsdDash::VERSION end get "/available", provides: :json do connection_pool.with do |conn| render_json conn.available end end # actual data API route get "/data", :provides => :json do cache_control :no_cache, :no_store statistics = parse_statistics range = parse_time_range return render_error('Invalid time range') unless range return render_error('Invalid metrics') if statistics.empty? options = { range: range.dup.map { |n| n * 1000 } } results = [] connection_pool.with do |conn| statistics.each do |datatype, metrics| metrics.each do |metric| statistic = "#{datatype}:#{metric}" json = conn.values(statistic, range) next unless json options[:interval] ||= json['interval'] options[:zero_fill] = statistic !~ /^gauges/i && !params[:no_zero_fill] points = json[statistic] || [] values = values_for_graph(points, options) results << { key: metric, type: datatype[0..-2], values: values } end end end render_json range: options[:range], interval: options[:interval], results: results end end end