Skip to content

Commit

Permalink
Slack event collector: fixed an issue where we get a Bad Request error (
Browse files Browse the repository at this point in the history
#31135)

* fixed an issue where we get a Bad Request error.

* pre-commit

* added test

* fixed Flake8 error

* fixed cr comments

* fixed cr comments

* update Docker image
  • Loading branch information
moishce authored Nov 30, 2023
1 parent ce8642d commit e6f3251
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ def prepare_query_params(params: dict) -> dict:

class Client(BaseClient):
def get_logs(self, query_params: dict) -> tuple[dict, list, str | None]:
demisto.debug(f'{query_params=}')
raw_response = self._http_request(
method='GET',
url_suffix='logs',
params=query_params,
retries=2,
)
demisto.debug(f'{raw_response=}')
events = raw_response.get('entries', [])
cursor = raw_response.get('response_metadata', {}).get('next_cursor')

Expand All @@ -52,7 +54,15 @@ def handle_pagination_first_batch(self, query_params: dict, last_run: dict) -> t
returns only the subsequent events (that weren't collected yet).
"""
query_params['cursor'] = last_run.pop('cursor', None)
_, events, cursor = self.get_logs(query_params)
try:
_, events, cursor = self.get_logs(query_params)
except DemistoException as e:
if 'Bad Request' in str(e):
demisto.debug('was returned: Error in API call [400] - Bad Request')
events, cursor = self.handle_pagination_first_batch_bad_cursor(query_params, last_run)
else:
raise Exception(e)

if last_run.get('first_id'):
for idx, event in enumerate(events):
if event.get('id') == last_run['first_id']:
Expand All @@ -61,6 +71,28 @@ def handle_pagination_first_batch(self, query_params: dict, last_run: dict) -> t
last_run.pop('first_id', None) # removing to make sure it won't be used in future runs
return events, cursor

def handle_pagination_first_batch_bad_cursor(self, query_params: dict, last_run: dict) -> tuple[list, str | None]:
"""
When we receive a "bad request" error
We try to rerun the request without the cursor
and go through the pages until we reach the first_id
"""
query_params.pop('cursor')
query_params['latest'] = last_run.get('first_created_at', None)
_, events, cursor = self.get_logs(query_params)
while last_run.get('first_id'):
for idx, event in enumerate(events):
if event.get('id') == last_run['first_id']:
events = events[idx:]
last_run.pop('first_id') # removing to make sure it won't be used in future runs
break
else:
query_params['cursor'] = cursor
_, events, cursor = self.get_logs(query_params)

continue
return events, cursor

def get_logs_with_pagination(self, query_params: dict, last_run: dict) -> list[dict]:
"""
Aggregates logs using cursor-based pagination, until one of the following occurs:
Expand Down Expand Up @@ -92,7 +124,8 @@ def get_logs_with_pagination(self, query_params: dict, last_run: dict) -> list[d
if len(aggregated_logs) == user_defined_limit:
demisto.debug(f'Reached the user-defined limit ({user_defined_limit}) - stopping.')
last_run['first_id'] = event.get('id')
cursor = query_params['cursor']
last_run['first_created_at'] = event.get('date_create')
cursor = query_params.get('cursor')
break

aggregated_logs.append(event)
Expand Down Expand Up @@ -214,6 +247,8 @@ def main() -> None: # pragma: no cover

elif command == 'fetch-events':
last_run = demisto.getLastRun()
demisto.debug(f'last run is: {last_run}')

events, last_run = fetch_events_command(client, params, last_run)

send_events_to_xsiam(
Expand All @@ -222,6 +257,7 @@ def main() -> None: # pragma: no cover
product=PRODUCT
)
demisto.setLastRun(last_run)
demisto.debug(f'Last run set to: {last_run}')

except Exception as e:
return_error(f'Failed to execute {command} command.\nError:\n{e}')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ script:
type: python
subtype: python3
isfetchevents: true
dockerimage: demisto/python3:3.10.12.63474
dockerimage: demisto/python3:3.10.13.81631
marketplaces:
- marketplacev2
fromversion: 6.8.0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import io
import json
import pytest

from copy import deepcopy
from SlackEventCollector import Client, prepare_query_params
from requests import Session

from CommonServerPython import DemistoException

""" Helpers """


def util_load_json(path):
with io.open(path, mode='r', encoding='utf-8') as f:
with open(path, encoding='utf-8') as f:
return json.loads(f.read())


Expand Down Expand Up @@ -124,6 +123,44 @@ def test_fetch_events_with_two_iterations(mocker):
assert mock_request.call_count == 2


def test_fetch_events_with_bad_cursor(mocker):
"""
Given:
- fetch-events command execution.
When:
- The first "get logs" was with a bad cursor, and we got a Bad Request error.
- Second, "get logs" we retry without a cursor and got a response with 3 events (that were already fetched)
- Third, "get logs" with a cursor from the previous run and got a response with 3 events (that only 1 was fetched)
Then:
- Make sure 2 events were returned.
- Make sure last_id is set.
"""
from SlackEventCollector import fetch_events_command, Client

err_msg = 'Error in API bad request[400] - Bad Request'

mock_response1 = MockResponse([
{'id': '3', 'date_create': 1521214345},
{'id': '2', 'date_create': 1521214343},
{'id': '1', 'date_create': 1521214343},
])
mock_response1.data['response_metadata'] = {'next_cursor': 'mock_cursor'}

mock_response2 = MockResponse([
{'id': '6', 'date_create': 1521214345},
{'id': '5', 'date_create': 1521214343},
{'id': '4', 'date_create': 1521214343},
])

mocker.patch.object(Client, '_http_request', side_effect=[DemistoException(err_msg, res={err_msg}),
mock_response1.data,
mock_response2.data])
events, last_run = fetch_events_command(Client(base_url=''), params={}, last_run={"first_id": "5"})

assert len(events) == 2
assert last_run == {'cursor': None, 'last_id': '5'}


def test_get_events(mocker):
"""
Given:
Expand Down
4 changes: 4 additions & 0 deletions Packs/Slack/ReleaseNotes/3_2_7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#### Integrations
##### Slack Event Collector
- Fixed an issue where the ***fetch-incidents*** command would fail due to a corrupted cursor stored in the last run.
- Updated the Docker image to: *demisto/python3:3.10.13.81631*.
2 changes: 1 addition & 1 deletion Packs/Slack/pack_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Slack",
"description": "Interact with Slack API - collect logs, send messages and notifications to your Slack team.",
"support": "xsoar",
"currentVersion": "3.2.6",
"currentVersion": "3.2.7",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
Expand Down

0 comments on commit e6f3251

Please sign in to comment.