class Kennel::Importer
Constants
- SORT_ORDER
- TITLES
Public Class Methods
new(api)
click to toggle source
# File lib/kennel/importer.rb, line 8 def initialize(api) @api = api end
Public Instance Methods
import(resource, id)
click to toggle source
# File lib/kennel/importer.rb, line 12 def import(resource, id) if ["screen", "dash"].include?(resource) raise ArgumentError, "resource 'screen' and 'dash' are deprecated, use 'dashboard'" end model = Kennel::Models::Record.subclasses.detect { |c| c.api_resource == resource } || raise(ArgumentError, "#{resource} is not supported") data = @api.show(model.api_resource, id) id = data.fetch(:id) # keep native value model.normalize({}, data) # removes id data[:id] = id title_field = TITLES.detect { |f| data[f] } title = data.fetch(title_field) title.tr!(Kennel::Models::Record::LOCK, "") # avoid double lock icon # calculate or reuse kennel_id data[:kennel_id] = if tracking_id = model.parse_tracking_id(data) model.remove_tracking_id(data) tracking_id.split(":").last else Kennel::Utils.parameterize(title) end case resource when "monitor" # flatten monitor options so they are all on the base which is how Monitor builds them data.merge!(data.delete(:options)) data.merge!(data.delete(:thresholds) || {}) # clean up values that are the default data.delete(:notify_no_data) if data[:notify_no_data] # Monitor uses true by default data.delete(:notify_audit) unless data[:notify_audit] # Monitor uses false by default # keep all values that are settable data = data.slice(*model.instance_methods) # make query use critical method if it matches critical = data[:critical] query = data[:query] if query && critical query.sub!(/([><=]) (#{Regexp.escape(critical.to_f.to_s)}|#{Regexp.escape(critical.to_i.to_s)})$/, "\\1 \#{critical}") end # using float in query is not allowed, so convert here data[:critical] = data[:critical].to_i if data[:type] == "event alert" data[:type] = "query alert" if data[:type] == "metric alert" when "dashboard" widgets = data[:widgets]&.flat_map { |widget| widget.dig(:definition, :widgets) || [widget] } widgets&.each do |widget| convert_widget_to_compact_format!(widget) dry_up_widget_metadata!(widget) (widget.dig(:definition, :markers) || []).each { |m| m[:label]&.delete! " " } end when "synthetics/tests" data[:locations] = :all if data[:locations].sort == Kennel::Models::SyntheticTest::LOCATIONS.sort else # noop end data.delete(:tags) if data[:tags] == [] # do not create super + [] call # simplify template_variables to array of string when possible if vars = data[:template_variables] vars.map! { |v| v[:default] == "*" && v[:prefix] == v[:name] ? v[:name] : v } end pretty = pretty_print(data).lstrip.gsub("\\#", "#") <<~RUBY #{model.name}.new( self, #{pretty} ) RUBY end
Private Instance Methods
convert_widget_to_compact_format!(widget)
click to toggle source
new api format is very verbose, so use old dry format when possible dd randomly chooses query0 or query1
# File lib/kennel/importer.rb, line 110 def convert_widget_to_compact_format!(widget) (widget.dig(:definition, :requests) || []).each do |request| next unless request.is_a?(Hash) next if request[:formulas] && ![[{ formula: "query1" }], [{ formula: "query0" }]].include?(request[:formulas]) next if request[:queries]&.size != 1 next if request[:queries].any? { |q| q[:data_source] != "metrics" } next if widget.dig(:definition, :type) != request[:response_format] request.delete(:formulas) request.delete(:response_format) request[:q] = request.delete(:queries).first.fetch(:query) end end
dry_up_widget_metadata!(widget)
click to toggle source
reduce duplication in imports by using dry `q: :metadata` when possible
# File lib/kennel/importer.rb, line 95 def dry_up_widget_metadata!(widget) (widget.dig(:definition, :requests) || []).each do |request| next unless request.is_a?(Hash) next unless metadata = request[:metadata] next unless query = request[:q]&.dup metadata.each do |m| next unless exp = m[:expression] query.sub!(exp, "") end request[:q] = :metadata if query.delete(", ") == "" end end
pretty_print(hash)
click to toggle source
# File lib/kennel/importer.rb, line 123 def pretty_print(hash) sort_widgets hash sort_hash(hash).map do |k, v| pretty_value = if v.is_a?(Hash) || (v.is_a?(Array) && !v.all? { |e| e.is_a?(String) }) # update answer here when changing https://stackoverflow.com/questions/8842546/best-way-to-pretty-print-a-hash # (exclude last indent gsub) pretty = JSON.pretty_generate(v) .gsub(": null", ": nil") .gsub(/(^\s*)"([a-zA-Z][a-zA-Z\d_]*)":/, "\\1\\2:") # "foo": 1 -> foo: 1 .gsub(/: \[\n\s+\]/, ": []") # empty arrays on a single line .gsub(/^/, " ") # indent .gsub('q: "metadata"', "q: :metadata") # bring symbols back "\n#{pretty}\n " elsif k == :message "\n <<~TEXT\n#{v.each_line.map { |l| l.strip.empty? ? "\n" : " #{l}" }.join}\n \#{super()}\n TEXT\n " elsif k == :tags " super() + #{v.inspect} " else " #{v.inspect} " end " #{k}: -> {#{pretty_value}}" end.join(",\n") end
sort_hash(hash)
click to toggle source
important to the front and rest deterministic
# File lib/kennel/importer.rb, line 160 def sort_hash(hash) Hash[hash.sort_by { |k, _| [SORT_ORDER.index(k) || 999, k] }] end
sort_widgets(outer)
click to toggle source
sort dashboard widgets + nesting
# File lib/kennel/importer.rb, line 151 def sort_widgets(outer) outer[:widgets]&.each do |widgets| definition = widgets[:definition] definition.replace sort_hash(definition) sort_widgets definition end end