class Flapjack::Data::Rule

Constants

STRATEGIES

Public Class Methods

jsonapi_associations() click to toggle source
# File lib/flapjack/data/rule.rb, line 376
def self.jsonapi_associations
  unless instance_variable_defined?('@jsonapi_associations')
    @jsonapi_associations = {
      :contact => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
        :post => true, :get => true,
        :number => :singular, :link => true, :includable => true,
        :descriptions => {
          :post => "Set a contact for a rule during rule creation (required).",
          :get => "Get the contact a rule belongs to."
        }
      ),
      :media => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
        :post => true, :get => true, :patch => true, :delete => true,
        :number => :multiple, :link => true, :includable => true,
        :descriptions => {
          :post => "Associate this rule with media on rule creation.",
          :get => "Get the media this rule is associated with.",
          :patch => "Update the media this rule is associated with.",
          :delete => "Delete associations between this rule and media."
        }
      ),
      :tags => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
        :post => true, :get => true, :patch => true, :delete => true,
        :number => :multiple, :link => true, :includable => true,
        :descriptions => {
          :post => "Associate tags with this rule.",
          :get => "Returns all tags linked to this rule.",
          :patch => "Update the tags associated with this rule.",
          :delete => "Delete associations between tags and this rule."
        }
      )
    }
    populate_association_data(@jsonapi_associations)
  end
  @jsonapi_associations
end
jsonapi_methods() click to toggle source
# File lib/flapjack/data/rule.rb, line 341
def self.jsonapi_methods
  @jsonapi_methods ||= {
    :post => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
      :attributes => [:name, :enabled, :blackhole, :strategy, :conditions_list,
        :time_restriction_ical],
      :descriptions => {
        :singular => "Create a notification rule.",
        :multiple => "Create notification rules."
      }
    ),
    :get => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
      :attributes => [:name, :enabled, :blackhole, :strategy, :conditions_list,
        :time_restriction_ical],
      :descriptions => {
        :singular => "Get data for a notification rule.",
        :multiple => "Get data for multiple notification rules."
      }
    ),
    :patch => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
      :attributes => [:name, :enabled, :blackhole, :strategy, :conditions_list,
        :time_restriction_ical],
      :descriptions => {
        :singular => "Update a notification rule.",
        :multiple => "Update notification rules."
      }
    ),
    :delete => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
      :descriptions => {
        :singular => "Delete a notification rule.",
        :multiple => "Delete notification rules."
      }
    )
  }
end
matching_checks(rule_ids) click to toggle source

called by medium.checks no global rules in the passed rule data rule_ids will be all acceptors (blackhole == false) or all rejectors (blackhole == true)

# File lib/flapjack/data/rule.rb, line 144
def self.matching_checks(rule_ids)
  m_checks = ['all_tags', 'any_tag', 'no_tag'].inject(nil) do |memo, strategy|
    tag_ids_by_rule_id = self.intersect(:strategy => strategy,
      :id => rule_ids).associated_ids_for(:tags)

    checks = checks_for_tag_match(strategy, tag_ids_by_rule_id)

    memo = if memo.nil?
      Flapjack::Data::Check.intersect(:id => checks)
    else
      memo.union(:id => checks)
    end
  end

  return Flapjack::Data::Check.empty if m_checks.nil?
  m_checks
end
matching_contact_ids(rule_ids, opts = {}) click to toggle source

called by check.contacts, rule_ids will be all acceptors (blackhole == false) or all rejectors (blackhole == true)

# File lib/flapjack/data/rule.rb, line 164
def self.matching_contact_ids(rule_ids, opts = {})
  time = opts[:time] || Time.now
  contact_rules = self.intersect(:id => rule_ids)

  matching_ids = apply_time_restriction(contact_rules, time).
    map(&:id)

  self.intersect(:id => matching_ids).
    associated_ids_for(:contact, :inversed => true).keys
end
matching_media_ids(rule_ids, opts = {}) click to toggle source

called by check.alerting_media, rule_ids will be all acceptors (blackhole == false) or all rejectors (blackhole == true)

# File lib/flapjack/data/rule.rb, line 177
def self.matching_media_ids(rule_ids, opts = {})
  time = opts[:time] || Time.now

  # if a rule has no media, it's irrelevant here
  media_rules = self.intersect(:id => rule_ids, :has_media => true)

  matching_ids = apply_time_restriction(media_rules, time).
    map(&:id)

  self.intersect(:id => matching_ids).
    associated_ids_for(:media).values.reduce(Set.new, :|)
end
media_added(rule_id, *m_ids) click to toggle source
# File lib/flapjack/data/rule.rb, line 128
def self.media_added(rule_id, *m_ids)
  rule = self.find_by_id!(rule_id)
  rule.has_media = true
  rule.save!
end
media_removed(rule_id, *m_ids) click to toggle source
# File lib/flapjack/data/rule.rb, line 134
def self.media_removed(rule_id, *m_ids)
  rule = self.find_by_id!(rule_id)
  rule.has_media = rule.media.empty?
  rule.save!
end
new(attributes = {}) click to toggle source
Calls superclass method
# File lib/flapjack/data/rule.rb, line 75
def initialize(attributes = {})
  super
  send(:"attribute=", 'has_media', false)
end
swagger_included_classes() click to toggle source
# File lib/flapjack/data/rule.rb, line 327
def self.swagger_included_classes
  # hack -- hardcoding for now
  [
    Flapjack::Data::Check,
    Flapjack::Data::Contact,
    Flapjack::Data::Medium,
    Flapjack::Data::Rule,
    Flapjack::Data::ScheduledMaintenance,
    Flapjack::Data::State,
    Flapjack::Data::Tag,
    Flapjack::Data::UnscheduledMaintenance
  ]
end

Protected Class Methods

apply_time_restriction(rules, time) click to toggle source
# File lib/flapjack/data/rule.rb, line 415
def self.apply_time_restriction(rules, time)
  # filter the rules by time restriction
  rule_ids_by_contact_id = rules.associated_ids_for(:contact, :inversed => true)

  rule_contacts = rule_ids_by_contact_id.empty? ? [] :
    Flapjack::Data::Contact.find_by_ids(*rule_ids_by_contact_id.keys)

  time_zones_by_rule_id = rule_contacts.each_with_object({}) do |c, memo|
    rule_ids_by_contact_id[c.id].each do |r_id|
      memo[r_id] = c.time_zone
    end
  end

  rules.select do |rule|
    rule.is_occurring_at?(time, time_zones_by_rule_id[rule.id])
  end
end

Private Class Methods

checks_for_tag_match(strategy, tag_ids_by_rule_id) click to toggle source
# File lib/flapjack/data/rule.rb, line 435
def self.checks_for_tag_match(strategy, tag_ids_by_rule_id)
  tag_ids_by_rule_id.values.inject(nil) do |memo, tag_ids|
    assocs = Flapjack::Data::Tag.intersect(:id => tag_ids).
      associations_for(:checks).values
    next memo if assocs.empty?

    checks = case strategy
    when 'all_tags'
      assocs.inject(Flapjack::Data::Check) do |c_memo, ca|
        c_memo = c_memo.intersect(:id => ca)
        c_memo
      end
    when 'any_tag'
      assocs.inject(nil) do |c_memo, ca|
        if c_memo.nil?
          Flapjack::Data::Check.intersect(:id => ca)
        else
          c_memo.union(:id => ca)
        end
      end
    when 'no_tag'
      assocs.inject(Flapjack::Data::Check) do |c_memo, ca|
        c_memo = c_memo.diff(:id => ca)
        c_memo
      end
    end

    memo = if memo.nil?
      Flapjack::Data::Check.intersect(:id => checks)
    else
      memo.union(:id => checks)
    end
    memo
  end
end

Public Instance Methods

is_occurring_at?(time, time_zone = Time.zone) click to toggle source

nil time_restriction matches

# File lib/flapjack/data/rule.rb, line 123
def is_occurring_at?(time, time_zone = Time.zone)
  return true if time_restriction.nil?
  time_restriction.occurring_at?(time.in_time_zone(time_zone))
end
time_restriction() click to toggle source
# File lib/flapjack/data/rule.rb, line 80
def time_restriction
  return if time_restriction_ical.nil? || !valid_time_restriction_ical?
  IceCube::Schedule.from_ical(time_restriction_ical)
end
time_restriction=(restriction) click to toggle source
# File lib/flapjack/data/rule.rb, line 85
def time_restriction=(restriction)
  if restriction.nil?
    self.time_restriction_ical = nil
    return
  end
  unless restriction.is_a?(IceCube::Schedule)
    raise "Invalid data type for time_restriction= (#{restriction.class.name})"
  end
  # ice_cube ignores time zone info when parsing ical, so we'll enforce UTC
  # and cast to the contact's preferred time zone as appropriate when using
  # (this should also handle the case of the user changing her timezone)
  restriction.start_time = restriction.start_time.nil? ? nil : restriction.start_time.utc
  restriction.end_time = restriction.end_time.nil? ? nil : restriction.end_time.utc
  self.time_restriction_ical = restriction.to_ical
end
valid_time_restriction_ical?() click to toggle source
# File lib/flapjack/data/rule.rb, line 101
def valid_time_restriction_ical?
  return true if time_restriction_ical.nil?
  wrapped_value = ['BEGIN:VCALENDAR',
                   'VERSION:2.0',
                   'PRODID:validationid',
                   'CALSCALE:GREGORIAN',
                   'BEGIN:VEVENT',
                   time_restriction_ical,
                   'END:VEVENT',
                   'END:VCALENDAR'].join("\n")

  # icalendar is noisy with errors
  old_icalendar_log_level = ::Icalendar.logger.level
  ::Icalendar.logger.level = ::Logger::FATAL
  icalendar = ::Icalendar.parse(wrapped_value)
  ::Icalendar.logger.level = old_icalendar_log_level

  !(icalendar.empty? || icalendar.first.events.empty? ||
    !icalendar.first.events.first.valid?)
end