def filter(self): """ Return true if the rule is applicable to the provided trigger instance. :rtype: ``bool`` """ LOG.info('Validating rule %s for %s.', self.rule.ref, self.trigger['name'], extra=self._base_logger_context) if not self.rule.enabled: if self.extra_info: LOG.info('Validation failed for rule %s as it is disabled.', self.rule.ref) return False criteria = self.rule.criteria is_rule_applicable = True if criteria and not self.trigger_instance.payload: return False payload_lookup = PayloadLookup(self.trigger_instance.payload) LOG.debug('Trigger payload: %s', self.trigger_instance.payload, extra=self._base_logger_context) for (criterion_k, criterion_v) in six.iteritems(criteria): is_rule_applicable, payload_value, criterion_pattern = self._check_criterion( criterion_k, criterion_v, payload_lookup) if not is_rule_applicable: if self.extra_info: criteria_extra_info = '\n'.join([ ' key: %s' % criterion_k, ' pattern: %s' % criterion_pattern, ' type: %s' % criterion_v['type'], ' payload: %s' % payload_value ]) LOG.info('Validation for rule %s failed on criteria -\n%s', self.rule.ref, criteria_extra_info, extra=self._base_logger_context) break if not is_rule_applicable: LOG.debug('Rule %s not applicable for %s.', self.rule.id, self.trigger['name'], extra=self._base_logger_context) return is_rule_applicable
def setUpClass(cls): cls.payload = PayloadLookup({ "pikachu": "Has no ears", "charmander": "Plays with fire", }) super(PayloadLookupTestCase, cls).setUpClass()
def search(value, criteria_pattern, criteria_condition, check_function): """ Search a list of values that match all child criteria. If condition is 'any', return a successful match if any items match all child criteria. If condition is 'all', return a successful match if ALL items match all child criteria. value: the payload list to search condition: one of: * any - return true if any payload items of the list match all criteria items * all - return true if all payload items of the list match all criteria items * all2any - return true if all payload items of the list match any criteria items * any2any - return true if any payload items match any criteria items pattern: a dictionary of criteria to apply to each item of the list This operator has O(n) algorithmic complexity in terms of number of child patterns. This operator has O(n) algorithmic complexity in terms of number of payload fields. However, it has O(n_patterns * n_payloads) algorithmic complexity, where: n_patterns = number of child patterns n_payloads = number of fields in payload It is therefore very easy to write a slow rule when using this operator. This operator should ONLY be used when trying to match a small number of child patterns and/or a small number of payload list elements. Other conditions (such as 'count', 'count_gt', 'count_gte', etc.) can be added as needed. Data from the trigger: { "fields": [ { "field_name": "Status", "to_value": "Approved" } ] } Example #1 --- criteria: trigger.fields: type: search # Controls whether this criteria has to match any or all items of the list condition: any # or all or all2any or any2any pattern: # Here our context is each item of the list # All of these patterns have to match the item for the item to match # These are simply other operators applied to each item in the list # "#" and text after are ignored. # This allows dictionary keys to be unique but refer to the same field item.field_name: type: "equals" pattern: "Status" item.to_value: type: "equals" pattern: "Approved" item.field_name#1: type: "greaterthan" pattern: 40 item.field_name#2: type: "lessthan" pattern: 50 """ if isinstance(value, dict): value = [value] payloadItemMatch = all patternMatch = all if criteria_condition == "any": payloadItemMatch = any elif criteria_condition == "all2any": patternMatch = any elif criteria_condition == "any2any": payloadItemMatch = any patternMatch = any elif criteria_condition != "all": raise UnrecognizedConditionError( "The '%s' condition is not recognized for type search, 'any', 'all', 'any2any'" " and 'all2any' are allowed" % criteria_condition) rtn = payloadItemMatch([ # any/all payload item can match patternMatch([ # Match any/all patterns check_function( child_criterion_k, child_criterion_v, PayloadLookup(child_payload, prefix=TRIGGER_ITEM_PAYLOAD_PREFIX), ) for child_criterion_k, child_criterion_v in six.iteritems( criteria_pattern) ]) for child_payload in value ]) return rtn
def search(value, criteria_pattern, criteria_condition, check_function): """ Search a list of values that match all child criteria. If condition is 'any', return a successful match if any items match all child criteria. If condition is 'all', return a successful match if ALL items match all child criteria. value: the payload list to search condition: one of: * any - return true if any items of the list match and false if none of them match * all - return true if all items of the list match and false if any of them do not match pattern: a dictionary of criteria to apply to each item of the list This operator has O(n) algorithmic complexity in terms of number of child patterns. This operator has O(n) algorithmic complexity in terms of number of payload fields. However, it has O(n_patterns * n_payloads) algorithmic complexity, where: n_patterns = number of child patterns n_payloads = number of fields in payload It is therefore very easy to write a slow rule when using this operator. This operator should ONLY be used when trying to match a small number of child patterns and/or a small number of payload list elements. Other conditions (such as 'count', 'count_gt', 'count_gte', etc.) can be added as needed. Data from the trigger: { "fields": [ { "field_name": "Status", "to_value": "Approved" } ] } And an example usage in criteria: --- criteria: trigger.fields: type: search # Controls whether this criteria has to match any or all items of the list condition: any # or all pattern: # Here our context is each item of the list # All of these patterns have to match the item for the item to match # These are simply other operators applied to each item in the list item.field_name: type: "equals" pattern: "Status" item.to_value: type: "equals" pattern: "Approved" """ if criteria_condition == 'any': # Any item of the list can match all patterns rtn = any([ # Any payload item can match all([ # Match all patterns check_function( child_criterion_k, child_criterion_v, PayloadLookup(child_payload, prefix=TRIGGER_ITEM_PAYLOAD_PREFIX)) for child_criterion_k, child_criterion_v in six.iteritems( criteria_pattern) ]) for child_payload in value ]) elif criteria_condition == 'all': # Every item of the list must match all patterns rtn = all([ # All payload items must match all([ # Match all patterns check_function( child_criterion_k, child_criterion_v, PayloadLookup(child_payload, prefix=TRIGGER_ITEM_PAYLOAD_PREFIX)) for child_criterion_k, child_criterion_v in six.iteritems( criteria_pattern) ]) for child_payload in value ]) else: raise UnrecognizedConditionError( "The '%s' search condition is not recognized, only 'any' " "and 'all' are allowed" % criteria_condition) return rtn
def filter(self): """ Return true if the rule is applicable to the provided trigger instance. :rtype: ``bool`` """ LOG.info( "Validating rule %s for %s.", self.rule.ref, self.trigger["name"], extra=self._base_logger_context, ) if not self.rule.enabled: if self.extra_info: LOG.info("Validation failed for rule %s as it is disabled.", self.rule.ref) return False criteria = self.rule.criteria is_rule_applicable = True if criteria and not self.trigger_instance.payload: return False payload_lookup = PayloadLookup(self.trigger_instance.payload) LOG.debug( "Trigger payload: %s", self.trigger_instance.payload, extra=self._base_logger_context, ) for (criterion_k, criterion_v) in six.iteritems(criteria): ( is_rule_applicable, payload_value, criterion_pattern, ) = self._check_criterion(criterion_k, criterion_v, payload_lookup) if not is_rule_applicable: if self.extra_info: criteria_extra_info = "\n".join([ " key: %s" % criterion_k, " pattern: %s" % criterion_pattern, " type: %s" % criterion_v["type"], " payload: %s" % payload_value, ]) LOG.info( "Validation for rule %s failed on criteria -\n%s", self.rule.ref, criteria_extra_info, extra=self._base_logger_context, ) break if not is_rule_applicable: LOG.debug( "Rule %s not applicable for %s.", self.rule.id, self.trigger["name"], extra=self._base_logger_context, ) return is_rule_applicable