Skip to content

Commit f212f11

Browse files
authored
Merge pull request #2376 from kamil-certat/rt_filter_out
ENH: RTCollector: Exclude by subject and multiple values
2 parents 2b1ed24 + 845944d commit f212f11

File tree

4 files changed

+70
-2
lines changed

4 files changed

+70
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ CHANGELOG
2727
### Bots
2828

2929
#### Collectors
30-
- `intelmq.bots.collector.rt`: restrict `python-rt` to be below version 3.0 due to introduced breaking changes.
30+
- `intelmq.bots.collector.rt`:
31+
- restrict `python-rt` to be below version 3.0 due to introduced breaking changes,
32+
- added support for `Subject NOT LIKE` queries,
33+
- added support for multiple values in ticket subject queries.
3134
- `intelmq.bots.collectors.rsync`: Support for optional private key, relative time parsing for the source path, extra rsync parameters and strict host key checking (PR#2241 by Mateo Durante).
3235

3336
#### Parsers

docs/user/bots.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,8 @@ If none of the filename matches apply, the contents of the first (RT-) "history"
574574
* `search_queue`: queue of the ticket to search for (default: `Incident Reports`)
575575
* `search_requestor`: the e-mail address of the requestor
576576
* `search_status`: status of the ticket to search for (default: `new`)
577-
* `search_subject_like`: part of the subject of the ticket to search for (default: `Report`)
577+
* `search_subject_like`: part of the subject of the ticket to search for (default: `Report`); use list for multiple required values,
578+
* `search_subject_notlike`: exclude subject containing given value, use list for multiple excluding values,
578579
* `set_status`: status to set the ticket to after processing (default: `open`). `false` or `null` to not set a different status.
579580
* `take_ticket`: whether to take the ticket (default: `true`)
580581
* `url_regex`: regular expression of an URL to search for in the ticket

intelmq/bots/collectors/rt/collector_rt.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class RTCollectorBot(CollectorBot, HttpMixin):
3535
search_requestor: Optional[str] = None
3636
search_status: str = "new"
3737
search_subject_like: str = "Report"
38+
search_subject_not_like: str = None
3839
set_status: str = "open"
3940
ssl_client_certificate: str = None # TODO: type should be pathlib.Path
4041
take_ticket: bool = True
@@ -47,8 +48,11 @@ class RTCollectorBot(CollectorBot, HttpMixin):
4748
'search_requestor': 'Requestor',
4849
'search_status': 'Status',
4950
'search_subject_like': 'Subject__like',
51+
'search_subject_not_like': 'Subject__notlike',
5052
}
5153

54+
RAW_QUERY_OP_MAPPING = {None: '=', 'like': ' LIKE ', 'notlike': ' NOT LIKE ', 'gt': '>'}
55+
5256
def init(self):
5357
if rt is None:
5458
raise MissingDependencyError("rt")
@@ -85,10 +89,16 @@ def process(self):
8589
else:
8690
kwargs = {}
8791

92+
convert_to_raw = False
8893
for parameter_name, rt_name in self.PARAMETER_MAPPING.items():
8994
parameter_value = getattr(self, parameter_name, None)
9095
if parameter_value:
9196
kwargs[rt_name] = parameter_value
97+
if isinstance(parameter_value, list):
98+
convert_to_raw = True
99+
100+
if convert_to_raw:
101+
kwargs = self._convert_to_raw_query(kwargs)
92102

93103
query = RT.search(order='Created', **kwargs)
94104
self.logger.info('%s results on search query.', len(query))
@@ -205,5 +215,19 @@ def process(self):
205215
if self.set_status:
206216
RT.edit_ticket(ticket_id, status=self.set_status)
207217

218+
@classmethod
219+
def _convert_to_raw_query(cls, kwargs: dict) -> str:
220+
parts = []
221+
for key, values in kwargs.items():
222+
if key == 'Queue':
223+
continue
224+
key_parts = key.split('__')
225+
field_name, op = key_parts[0], None if len(key_parts) == 1 else key_parts[-1]
226+
values = values if isinstance(values, list) else [values]
227+
for value in values:
228+
parts.append(f"({field_name}{cls.RAW_QUERY_OP_MAPPING[op]}\'{value}\')")
229+
230+
return {'Queue': kwargs.get('Queue'), 'raw_query': ' AND '.join(parts)}
231+
208232

209233
BOT = RTCollectorBot

intelmq/tests/bots/collectors/rt/test_collector.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,45 @@ def test_url_zip(self):
9999
self.assertMessageEqual(1, REPORT_URL2)
100100

101101

102+
class TestRTRawQuery(unittest.TestCase):
103+
def test_simple(self):
104+
kwargs = {
105+
'Status': 'active',
106+
'Subject__like': 'Report',
107+
'Subject__notlike': 'except',
108+
'Created__gt': '2023-01-01',
109+
'Queue': 'IR',
110+
}
111+
112+
raw_query = RTCollectorBot._convert_to_raw_query(kwargs)
113+
114+
expected_query = ("(Status=\'active\')"
115+
" AND (Subject LIKE \'Report\')"
116+
" AND (Subject NOT LIKE \'except\')"
117+
" AND (Created>\'2023-01-01\')")
118+
119+
self.assertEqual(raw_query, {"Queue": "IR", "raw_query": expected_query})
120+
121+
def test_multiple_values(self):
122+
kwargs = {
123+
'Status': 'active',
124+
'Subject__like': ['Report', 'Report 2'],
125+
'Subject__notlike': ['except', 'except2'],
126+
'Created__gt': '2023-01-01',
127+
'Queue': 'IR',
128+
}
129+
130+
raw_query = RTCollectorBot._convert_to_raw_query(kwargs)
131+
132+
expected_query = ("(Status=\'active\')"
133+
" AND (Subject LIKE \'Report\')"
134+
" AND (Subject LIKE \'Report 2\')"
135+
" AND (Subject NOT LIKE \'except\')"
136+
" AND (Subject NOT LIKE \'except2\')"
137+
" AND (Created>\'2023-01-01\')")
138+
139+
self.assertEqual(raw_query, {"Queue": "IR", "raw_query": expected_query})
140+
141+
102142
if __name__ == '__main__': # pragma: no cover
103143
unittest.main()

0 commit comments

Comments
 (0)