From e085b87373d5959bd906f41533a0c5320a5bb3d0 Mon Sep 17 00:00:00 2001 From: "Matthew G. Monteleone" Date: Mon, 19 Sep 2022 16:15:10 -0700 Subject: [PATCH] Completed #147, adding ability to retrieve events by type. Added `all_by_type` and `since_by_type` methods, which take a passed `AtlasEventType. Added two tests, passing. --- atlasapi/__init__.py | 2 +- atlasapi/atlas.py | 108 ++++++++++++++++++++++++++++++++++++++++++- atlasapi/settings.py | 2 +- setup.py | 2 +- tests/test_events.py | 26 ++++++++++- 5 files changed, 133 insertions(+), 7 deletions(-) diff --git a/atlasapi/__init__.py b/atlasapi/__init__.py index 689bfe3..23bb6ac 100644 --- a/atlasapi/__init__.py +++ b/atlasapi/__init__.py @@ -15,4 +15,4 @@ # __init__.py # Version of the realpython-reader package -__version__ = "2.0.3" +__version__ = "2.0.4" diff --git a/atlasapi/atlas.py b/atlasapi/atlas.py index 7da1068..5e66eb1 100644 --- a/atlasapi/atlas.py +++ b/atlasapi/atlas.py @@ -25,12 +25,13 @@ from dateutil.relativedelta import relativedelta from atlasapi.specs import Host, ListOfHosts, DatabaseUsersUpdatePermissionsSpecs, DatabaseUsersPermissionsSpecs, \ ReplicaSetTypes -from atlasapi.measurements import AtlasMeasurementTypes, AtlasMeasurementValue, AtlasMeasurement, OptionalAtlasMeasurement +from atlasapi.measurements import AtlasMeasurementTypes, AtlasMeasurementValue, AtlasMeasurement, \ + OptionalAtlasMeasurement from typing import Union, Iterator, List, Optional from atlasapi.atlas_types import OptionalInt, OptionalBool, ListofDict from atlasapi.clusters import ClusterConfig, ShardedClusterConfig, AtlasBasicReplicaSet, \ InstanceSizeName, AdvancedOptions, TLSProtocols -from atlasapi.events import atlas_event_factory, ListOfEvents +from atlasapi.events import atlas_event_factory, ListOfEvents, AtlasEventTypes, AtlasEvent import logging from typing import Union, Iterable, Set, BinaryIO, Generator, Iterator from atlasapi.errors import ErrAtlasUnauthorized, ErrAtlasBadRequest @@ -943,6 +944,62 @@ def _get_all_project_events(self, since_datetime: datetime = None, pageNum: int return_val = self.atlas.network.get(Settings.BASE_URL + uri) return return_val + def _get_project_events_by_type(self, event_type: AtlasEventTypes, since_datetime: datetime = None, + pageNum: int = Settings.pageNum, + itemsPerPage: int = Settings.itemsPerPage, + iterable: bool = False) -> Union[List[dict], Iterable[AtlasEvent]]: + """Get All Project Events For A Single type + + Internal use only, actual data retrieval comes from properties all + url: https://docs.atlas.mongodb.com/reference/api/events-projects-get-all/ + + Keyword Args: + event_type (AtlasEventType): + since_datetime (datetime): + pageNum (int): Page number + itemsPerPage (int): Number of Users per Page + iterable (bool): To return an iterable high level object instead of a low level API response + + Returns: + ListOfEvents or dict: Iterable object representing this function OR Response payload + + Raises: + ErrPaginationLimits: Out of limits + :rtype: Union[ListOfEvents, dict] + :type iterable: OptionalBool + :type itemsPerPage: OptionalInt + :type pageNum: OptionalInt + + """ + + # Check limits and raise an Exception if needed + ErrPaginationLimits.checkAndRaise(pageNum, itemsPerPage) + + if iterable: + item_list = list(EventsGetForProjectAndType(self.atlas, event_type, since_datetime, + pageNum, itemsPerPage)) + obj_list: ListOfEvents = list() + for item in item_list: + obj_list.append(atlas_event_factory(item)) + return obj_list + + if since_datetime: + uri = Settings.api_resources["Events"]["Get Project Events Since Date"].format( + min_date=since_datetime.isoformat(), + group_id=self.atlas.group, + page_num=pageNum, + items_per_page=itemsPerPage) + f'&eventType={event_type.name}' + + return_val = self.atlas.network.get(Settings.BASE_URL + uri) + + else: + uri = Settings.api_resources["Events"]["Get All Project Events"].format( + group_id=self.atlas.group, + page_num=pageNum, + items_per_page=itemsPerPage) + f'&eventType={event_type.name}' + return_val = self.atlas.network.get(Settings.BASE_URL + uri) + return return_val + @property def all(self) -> ListOfEvents: """ @@ -954,6 +1011,18 @@ def all(self) -> ListOfEvents: """ return self._get_all_project_events(iterable=True) + def all_by_type(self, event_type: AtlasEventTypes) -> Iterable[AtlasEvent]: + """Returns all events for the passed AtlasEventType + + Args: + event_type (AtlasEventTypes): + + Returns: + Iterable[AtlasEvent] + + """ + return self._get_project_events_by_type(event_type=event_type, iterable=True) + def since(self, since_datetime: datetime) -> ListOfEvents: """ Returns all events since the passed datetime. (UTC) @@ -963,6 +1032,18 @@ def since(self, since_datetime: datetime) -> ListOfEvents: """ return self._get_all_project_events(iterable=True, since_datetime=since_datetime) + def since_by_type(self, since_datetime: datetime, event_type: AtlasEventTypes): + """Returns all events since the passed detetime (UTC) for the passed AtlasEvent Type + + Args: + since_datetime (datetime): + event_type (AtlasEventTypes): + + Returns: + + """ + return self._get_project_events_by_type(event_type=event_type, since_datetime=since_datetime, iterable=True) + class _DatabaseUsers: """Database Users API @@ -2156,6 +2237,29 @@ def fetch(self, pageNum, itemsPerPage): return self._get_all_project_events(self.since_datetime, pageNum, itemsPerPage) +# noinspection PyProtectedMember +class EventsGetForProjectAndType(AtlasPagination): + + def __init__(self, atlas: Atlas, event_type: AtlasEventTypes, since_datetime: datetime, + pageNum: int, itemsPerPage: int): + super().__init__(atlas, self.fetch, pageNum, itemsPerPage) + self._get_project_events_by_type = atlas.Events._get_project_events_by_type + self.since_datetime = since_datetime + self.event_type = event_type + + def fetch(self, pageNum, itemsPerPage): + """Intermediate fetching + + Args: + pageNum (int): Page number + itemsPerPage (int): Number of Events per Page + + Returns: + dict: Response payload + """ + return self._get_project_events_by_type(self.event_type, self.since_datetime, pageNum, itemsPerPage) + + class DatabaseUsersGetAll(AtlasPagination): """Pagination for Database User : Get All""" diff --git a/atlasapi/settings.py b/atlasapi/settings.py index c6e2f0a..017ff3f 100644 --- a/atlasapi/settings.py +++ b/atlasapi/settings.py @@ -56,7 +56,7 @@ class Settings: "{host}:{port}/databases" }, "Events": { - "Get All Project Events": URI_STUB + "/groups/{group_id}/events??includeRaw=true&pageNum={page_num}" + "Get All Project Events": URI_STUB + "/groups/{group_id}/events?includeRaw=true&pageNum={page_num}" "&itemsPerPage={items_per_page}", "Get Project Events Since Date": URI_STUB + "/groups/{group_id}/events?includeRaw=true&pageNum={" "page_num}&itemsPerPage={items_per_page}&minDate={" diff --git a/setup.py b/setup.py index 40c0282..ea92b1a 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='atlasapi', - version='2.0.3', + version='2.0.4', python_requires='>=3.7', packages=find_packages(exclude=("tests",)), install_requires=['requests', 'python-dateutil', 'isodate', 'future', 'pytz','coolname', 'humanfriendly', 'nose'], diff --git a/tests/test_events.py b/tests/test_events.py index d05b88f..dc2f320 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -14,6 +14,7 @@ import logging from time import sleep from datetime import datetime, timedelta, timezone +from atlasapi.events import AtlasEventTypes, AtlasEvent, _AtlasBaseEvent USER = getenv('ATLAS_USER', None) API_KEY = getenv('ATLAS_KEY', None) @@ -48,7 +49,6 @@ def test_01_get_project_events_since(self): test_01_get_project_events_since.basic = True - def test_02_since(self): test_datetime = datetime.utcnow() - timedelta(hours=12) verbose_logger.info(f'Events Since (public) {test_datetime.isoformat()}') @@ -71,5 +71,27 @@ def test_04_CPS(self): self.assertIsInstance(out, list) for each in out: if type(each) == events.AtlasCPSEvent: - self.assertIsInstance(each,events.AtlasCPSEvent) + self.assertIsInstance(each, events.AtlasCPSEvent) + test_04_CPS.basic = True + + def test_05_All_Events_By_Type(self): + out = self.a.Events.all_by_type(event_type=AtlasEventTypes.CLUSTER_UPDATE_COMPLETED) + count = 0 + for each_event in out: + count += 1 + self.assertIsInstance(each_event, _AtlasBaseEvent) + pprint(f"Found {count} events.") + + test_05_All_Events_By_Type.basic = True + + def test_06_Events_Since_By_Type(self): + out = self.a.Events.since_by_type(event_type=AtlasEventTypes.CLUSTER_UPDATE_COMPLETED, + since_datetime=datetime(2022, 1, 1)) + count = 0 + for each_event in out: + count += 1 + self.assertIsInstance(each_event, _AtlasBaseEvent) + pprint(f"Found {count} events.") + + test_06_Events_Since_By_Type.basic = True