Skip to content

Commit

Permalink
Replace manual cache with shelf-backed cookie jar.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Dec 1, 2022
1 parent 5a52d41 commit a7de8ed
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 116 deletions.
3 changes: 2 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ def instance_client(request):
return

request.instance.client = jaraco.abode.Client(
username='foobar', password='deadbeef', disable_cache=True
username='foobar',
password='deadbeef',
)


Expand Down
22 changes: 0 additions & 22 deletions jaraco/abode/cache.py

This file was deleted.

9 changes: 0 additions & 9 deletions jaraco/abode/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import jaraco.abode
from . import Client
from .helpers import urls
from .helpers import constants as CONST
from .helpers import timeline as TIMELINE

_LOGGER = logging.getLogger('abodecl')
Expand Down Expand Up @@ -75,13 +74,6 @@ def build_parser():

parser.add_argument('--mfa', help='Multifactor authentication code')

parser.add_argument(
'--cache',
metavar='pickle_file',
help='Create/update/use a pickle cache for the username and password.',
default=CONST.CACHE_PATH,
)

parser.add_argument(
'--mode',
help='Output current alarm mode',
Expand Down Expand Up @@ -222,7 +214,6 @@ def _create_client_instance(args):
username=args.username,
password=args.password,
get_devices=args.mfa is None,
cache_path=args.cache,
)


Expand Down
57 changes: 13 additions & 44 deletions jaraco/abode/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
"""

import logging
import os
import uuid

from more_itertools import always_iterable
from requests_toolbelt import sessions
from requests.exceptions import RequestException
from jaraco.net.http import cookies
from jaraco.functools import retry

import jaraco
from .automation import Automation
Expand All @@ -18,15 +19,23 @@
from .helpers import urls
from .helpers import constants as CONST
from .helpers import errors as ERROR
from . import collections as COLLECTIONS
from . import cache as CACHE
from .devices.base import Device
from . import settings
from . import config


_LOGGER = logging.getLogger(__name__)


@retry(
retries=1,
cleanup=lambda: config.paths.user_data.joinpath('cookies.db').unlink(),
trap=Exception,
)
def _cookies():
return cookies.ShelvedCookieJar.create(config.paths.user_data)


class Client:
"""Client to an Abode system."""

Expand All @@ -37,16 +46,12 @@ def __init__(
auto_login=False,
get_devices=False,
get_automations=False,
cache_path=CONST.CACHE_PATH,
disable_cache=False,
):
"""Init Abode object."""
self._session = None
self._token = None
self._panel = None
self._user = None
self._cache_path = cache_path
self._disable_cache = disable_cache
self._username = username
self._password = password

Expand All @@ -58,20 +63,8 @@ def __init__(

self._automations = None

# Create a requests session to persist the cookies
self._session = sessions.BaseUrlSession(urls.BASE)

# Create a new cache template
self._cache = {}

# Load and merge an existing cache
if not disable_cache:
self._load_cache()

# Load persisted cookies (which contains the UUID and the session ID)
# if available
if self._cache.get('cookies'):
self._session.cookies = self._cache['cookies']
self._session.cookies = _cookies()

if auto_login:
self.login()
Expand Down Expand Up @@ -117,11 +110,6 @@ def login(self, username=None, password=None, mfa_code=None): # noqa: C901

raise AuthenticationException(ERROR.UNKNOWN_MFA_TYPE)

# Persist cookies (which contains the UUID and the session ID) to disk
if self._session.cookies.get_dict():
self._cache['cookies'] = self._session.cookies
self._save_cache()

oauth_response = self._session.get(urls.OAUTH_TOKEN)
AuthenticationException.raise_for(oauth_response)
oauth_response_object = oauth_response.json()
Expand Down Expand Up @@ -337,22 +325,3 @@ def _get_session(self):
self.send_request("get", urls.PANEL)

return self._session

def _load_cache(self):
"""Load existing cache and merge for updating if required."""
if not self._disable_cache and os.path.exists(self._cache_path):
_LOGGER.debug("Cache found at: %s", self._cache_path)
loaded_cache = CACHE.load_cache(self._cache_path)

if loaded_cache:
COLLECTIONS.update(self._cache, loaded_cache)
else:
_LOGGER.debug("Removing invalid cache file: %s", self._cache_path)
os.remove(self._cache_path)

self._save_cache()

def _save_cache(self):
"""Trigger a cache save."""
if not self._disable_cache:
CACHE.save_cache(self._cache, self._cache_path)
11 changes: 0 additions & 11 deletions jaraco/abode/collections.py

This file was deleted.

4 changes: 4 additions & 0 deletions jaraco/abode/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import app_paths


paths = app_paths.AppPaths.get_paths(appname='Abode', appauthor=False)
2 changes: 0 additions & 2 deletions jaraco/abode/helpers/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
CACHE_PATH = './abode.pickle'

# NOTIFICATION CONSTANTS
SOCKETIO_URL = 'wss://my.goabode.com/socket.io/'

Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ install_requires =
more_itertools
importlib_resources
bx_py_utils
app_paths

[options.packages.find]
exclude =
Expand Down
52 changes: 25 additions & 27 deletions tests/test_abode.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
Tests the system initialization and attributes of the main Abode system.
"""
import os

import pytest
import requests

import jaraco.abode
import jaraco.abode.helpers.constants as CONST
from jaraco.abode.helpers import urls
from jaraco.abode import settings
from jaraco.abode import config

from . import mock as MOCK
from .mock import login as LOGIN
Expand All @@ -24,14 +23,22 @@


@pytest.fixture
def cache_path(tmp_path, request):
request.instance.cache_path = tmp_path / 'cache.pickle'
def data_path(tmp_path, monkeypatch):
class Paths:
user_data_path = tmp_path / 'user_data'

@property
def user_data(self):
self.user_data_path.mkdir(exist_ok=True)
return self.user_data_path

monkeypatch.setattr(config, 'paths', Paths())


@pytest.fixture(autouse=True)
def abode_objects(request):
self = request.instance
self.client_no_cred = jaraco.abode.Client(disable_cache=True)
self.client_no_cred = jaraco.abode.Client()


USERNAME = 'foobar'
Expand Down Expand Up @@ -88,7 +95,6 @@ def tests_auto_login(self, m):
password='buzz',
auto_login=True,
get_devices=False,
disable_cache=True,
)

assert client._username == 'fizz'
Expand Down Expand Up @@ -123,7 +129,6 @@ def tests_auto_fetch(self, m):
auto_login=False,
get_devices=True,
get_automations=True,
disable_cache=True,
)

assert client._username == 'fizz'
Expand Down Expand Up @@ -482,7 +487,7 @@ def tests_siren_settings(self, m):
with pytest.raises(jaraco.abode.Exception):
self.client.set_setting(settings.SIREN_TAMPER_SOUNDS, "foobar")

@pytest.mark.usefixtures('cache_path')
@pytest.mark.usefixtures('data_path')
def tests_cookies(self, m):
"""Check that cookies are saved and loaded successfully."""
cookies = dict(SESSION='COOKIE')
Expand All @@ -498,18 +503,17 @@ def tests_cookies(self, m):
password='buzz',
auto_login=False,
get_devices=False,
disable_cache=False,
cache_path=self.cache_path,
)

client.login()

# Test that our cookies are fully realized prior to login

assert client._cache['cookies'] is not None
assert client._session.cookies

# Test that we now have a cookies file
assert os.path.exists(self.cache_path)
cookies_file = config.paths.user_data / 'cookies.db'
assert cookies_file.exists()

# Copy the current cookies
saved_cookies = client._session.cookies
Expand All @@ -520,14 +524,12 @@ def tests_cookies(self, m):
password='buzz',
auto_login=False,
get_devices=False,
disable_cache=False,
cache_path=self.cache_path,
)

# Test that the cookie data is the same
assert client._session.cookies == saved_cookies
assert str(client._session.cookies) == str(saved_cookies)

@pytest.mark.usefixtures('cache_path')
@pytest.mark.usefixtures('data_path')
def test_empty_cookies(self, m):
"""Check that empty cookies file is loaded successfully."""
cookies = dict(SESSION='COOKIE')
Expand All @@ -538,22 +540,20 @@ def test_empty_cookies(self, m):
m.get(urls.PANEL, json=PANEL.get_response_ok())

# Create an empty file
self.cache_path.write_text('')
cookie_file = config.paths.user_data / 'cookies.db'

# Cookies are created
empty_client = jaraco.abode.Client(
jaraco.abode.Client(
username='fizz',
password='buzz',
auto_login=True,
get_devices=False,
disable_cache=False,
cache_path=self.cache_path,
)

# Test that some cache exists
assert empty_client._cache['cookies']
# Test that some cookie data exists
assert cookie_file.read_bytes()

@pytest.mark.usefixtures('cache_path')
@pytest.mark.usefixtures('data_path')
def test_invalid_cookies(self, m):
"""Check that empty cookies file is loaded successfully."""
cookies = dict(SESSION='COOKIE')
Expand All @@ -564,17 +564,15 @@ def test_invalid_cookies(self, m):
m.get(urls.PANEL, json=PANEL.get_response_ok())

# Create an invalid pickle file
self.cache_path.write_text('Invalid file goes here')
config.paths.user_data.joinpath('cookies.db').write_text('invalid cookies')

# Cookies are created
empty_client = jaraco.abode.Client(
username='fizz',
password='buzz',
auto_login=True,
get_devices=False,
disable_cache=False,
cache_path=self.cache_path,
)

# Test that some cache exists
assert empty_client._cache['cookies']
assert empty_client._session.cookies

0 comments on commit a7de8ed

Please sign in to comment.