diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4f2005ac..553404e7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,8 +28,6 @@ jobs: # https://github.com/mamba-org/setup-micromamba/issues/227 micromamba-version: 1.5.10-0 environment-file: ${{ matrix.env }} - cache-environment: true - cache-downloads: true - name: Test env: diff --git a/ecoscope/io/async_earthranger.py b/ecoscope/io/async_earthranger.py index ecf1b901..96b993d8 100644 --- a/ecoscope/io/async_earthranger.py +++ b/ecoscope/io/async_earthranger.py @@ -6,6 +6,7 @@ import ecoscope from ecoscope.io.utils import to_hex from ecoscope.io.earthranger_utils import clean_kwargs, to_gdf, clean_time_cols +from erclient.client import ERClientException, ERClientNotFound try: from erclient.client import AsyncERClient @@ -30,6 +31,28 @@ def __init__(self, sub_page_size=4000, tcp_limit=5, **kwargs): kwargs["client_id"] = kwargs.get("client_id", "das_web_client") super().__init__(**kwargs) + @classmethod + async def create(cls, **kwargs): + client = cls(**kwargs) + + await client._init_client() + return client + + async def _init_client(self): + if not self.auth: + try: + await self.login() + except ERClientNotFound: + raise ERClientNotFound("Failed login. Check Stack Trace for specific reason.") + else: + try: + await self.get_me() + except ERClientException: + raise ERClientException("Authorization token is invalid or expired.") + + async def get_me(self): + return await self._get("user/me", params={}) + async def get_sources( self, manufacturer_id=None, diff --git a/ecoscope/io/earthranger.py b/ecoscope/io/earthranger.py index ca2b3d50..6efc5774 100644 --- a/ecoscope/io/earthranger.py +++ b/ecoscope/io/earthranger.py @@ -34,22 +34,29 @@ def __init__(self, sub_page_size=4000, tcp_limit=5, **kwargs): self.tcp_limit = tcp_limit kwargs["client_id"] = kwargs.get("client_id", "das_web_client") super().__init__(**kwargs) - try: - self.login() - except ERClientNotFound: - raise ERClientNotFound("Failed login. Check Stack Trace for specific reason.") + + if not self.auth: + try: + self.login() + except ERClientNotFound: + raise ERClientNotFound("Failed login. Check Stack Trace for specific reason.") + else: + try: + self.get_me() + except ERClientException: + raise ERClientException("Authorization token is invalid or expired.") def _token_request(self, payload): response = requests.post(self.token_url, data=payload) if response.ok: - self.auth = json.loads(response.text) + self.auth = response.json() expires_in = int(self.auth["expires_in"]) - 5 * 60 self.auth_expires = pytz.utc.localize(datetime.datetime.utcnow()) + datetime.timedelta(seconds=expires_in) return True self.auth = None self.auth_expires = pytz.utc.localize(datetime.datetime.min) - raise ERClientNotFound(json.loads(response.text)["error_description"]) + raise ERClientNotFound(response.json().get("error_description", "invalid token")) """ GET Functions diff --git a/tests/test_asyncearthranger_io.py b/tests/test_asyncearthranger_io.py index 8eafb57d..2703edb5 100644 --- a/tests/test_asyncearthranger_io.py +++ b/tests/test_asyncearthranger_io.py @@ -2,7 +2,9 @@ import pandas as pd import pytest +import pytest_asyncio import ecoscope +from erclient import ERClientException if not pytest.earthranger: pytest.skip( @@ -11,12 +13,12 @@ ) -@pytest.fixture -def er_io_async(): +@pytest_asyncio.fixture +async def er_io_async(): ER_SERVER = "https://mep-dev.pamdas.org" ER_USERNAME = os.getenv("ER_USERNAME") ER_PASSWORD = os.getenv("ER_PASSWORD") - er_io = ecoscope.io.AsyncEarthRangerIO(server=ER_SERVER, username=ER_USERNAME, password=ER_PASSWORD) + er_io = await ecoscope.io.AsyncEarthRangerIO.create(server=ER_SERVER, username=ER_USERNAME, password=ER_PASSWORD) return er_io @@ -251,3 +253,36 @@ async def test_display_map(er_io_async): await er_io_async.get_event_type_display_name(event_type="shot_rep", event_property="shotrep_timeofshot") == "Time when shot was heard" ) + + +@pytest.mark.asyncio +async def test_existing_token(er_io_async): + await er_io_async.login() + new_client = ecoscope.io.AsyncEarthRangerIO( + service_root=er_io_async.service_root, + token_url=er_io_async.token_url, + token=er_io_async.auth.get("access_token"), + ) + + sources = await new_client.get_sources_dataframe() + assert not sources.empty + + +@pytest.mark.asyncio +async def test_existing_token_expired(er_io_async): + await er_io_async.login() + token = er_io_async.auth.get("access_token") + await er_io_async.refresh_token() + + new_client = ecoscope.io.AsyncEarthRangerIO( + service_root=er_io_async.service_root, token_url=er_io_async.token_url, token=token + ) + + with pytest.raises(ERClientException): + await new_client.get_sources_dataframe() + + +@pytest.mark.asyncio +async def test_get_me(er_io_async): + me = await er_io_async.get_me() + assert me.get("username") diff --git a/tests/test_earthranger_io.py b/tests/test_earthranger_io.py index add3d6c0..f18d92ce 100644 --- a/tests/test_earthranger_io.py +++ b/tests/test_earthranger_io.py @@ -8,6 +8,7 @@ from shapely.geometry import Point import ecoscope +from erclient import ERClientException if not pytest.earthranger: pytest.skip( @@ -291,3 +292,23 @@ def test_get_subjects_chunking(er_io): chunked_request_result = er_io.get_subjects(id=subject_ids, max_ids_per_request=1) pd.testing.assert_frame_equal(single_request_result, chunked_request_result) + + +def test_existing_token(er_io): + new_client = ecoscope.io.EarthRangerIO( + service_root=er_io.service_root, token_url=er_io.token_url, token=er_io.auth.get("access_token") + ) + + events = new_client.get_patrol_events( + since=pd.Timestamp("2017-01-01").isoformat(), + until=pd.Timestamp("2017-04-01").isoformat(), + ) + assert not events.empty + + +def test_existing_token_expired(er_io): + token = er_io.auth.get("access_token") + er_io.refresh_token() + + with pytest.raises(ERClientException, match="Authorization token is invalid or expired."): + ecoscope.io.EarthRangerIO(service_root=er_io.service_root, token_url=er_io.token_url, token=token)