Skip to content

Commit

Permalink
Merge pull request #33 from jmolinski/refactor_http
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
jmolinski committed Apr 9, 2019
2 parents 3ae17c7 + 505e84e commit d0e3cf5
Show file tree
Hide file tree
Showing 18 changed files with 603 additions and 600 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ If the response is corrupt (can't be parsed) the parser will raise `trakt.core.e

---
Todo 0.1.0:
- http component retries
- docs
- pypi release

Expand All @@ -89,3 +88,4 @@ Todo 0.2.0:
- methods on models (episode.rate() etc)
- user profile
- caching (networks, countries etc)
- http component retries
3 changes: 2 additions & 1 deletion tests/inferfaces_tests/test_checkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from tests.test_data.episodes import EPISODE
from tests.test_data.movies import MOVIE1
from tests.test_data.shows import SHOW
from tests.utils import USER, mk_mock_client
from tests.utils import USER, get_last_req, mk_mock_client
from trakt.core.exceptions import ArgumentError, NotAuthenticated
from trakt.core.json_parser import parse_tree
from trakt.core.models import Episode, Movie, Show
Expand Down Expand Up @@ -94,3 +94,4 @@ def test_delete_checkins():
client.set_user(USER)

client.checkin.delete_active_checkins()
assert get_last_req(client.http)["method"] == "DELETE"
12 changes: 3 additions & 9 deletions tests/inferfaces_tests/test_recommendations.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from tests.test_data.movies import MOVIE1, MOVIES
from tests.test_data.shows import SHOW
from tests.utils import USER, mk_mock_client
from tests.utils import USER, get_last_req, mk_mock_client
from trakt.core.exceptions import ArgumentError, NotAuthenticated
from trakt.core.json_parser import parse_tree
from trakt.core.models import Movie, Show
Expand Down Expand Up @@ -39,10 +39,7 @@ def test_hide_movie():
client.recommendations.hide_movie(movie=movie)
client.recommendations.hide_movie(movie=movie.ids.trakt)

reqs = list(client.http._requests.req_map.items())[0]
req = reqs[1][1] # [(path, data)] -> data

assert req["method"] == "DELETE"
assert get_last_req(client.http)["method"] == "DELETE"


def test_recommendations_shows():
Expand Down Expand Up @@ -72,7 +69,4 @@ def test_hide_show():

client.recommendations.hide_show(show=show)

reqs = list(client.http._requests.req_map.items())[0]
req = reqs[1][0] # [(path, data)] -> data

assert req["method"] == "DELETE"
assert get_last_req(client.http)["method"] == "DELETE"
17 changes: 7 additions & 10 deletions tests/inferfaces_tests/test_shows.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,13 @@ def test_watching(shows_client):
assert watching[0].name == USER["name"]


def test_next_episode():
def next_ep_responses():
yield MockResponse({}, 204)
yield MockResponse(EXTENDED_EPISODE, 200)
def next_last_ep_responses():
yield MockResponse({}, 204)
yield MockResponse(EXTENDED_EPISODE, 200)


client = mk_mock_client({".*shows.*": next_ep_responses()})
def test_next_episode():
client = mk_mock_client({".*shows.*": next_last_ep_responses()})

no_ep = client.shows.get_next_episode(show=123, extended=True)
ep = client.shows.get_next_episode(show=123, extended=True)
Expand All @@ -169,11 +170,7 @@ def next_ep_responses():


def test_last_episode():
def next_ep_responses():
yield MockResponse({}, 204)
yield MockResponse(EXTENDED_EPISODE, 200)

client = mk_mock_client({".*shows.*": next_ep_responses()})
client = mk_mock_client({".*shows.*": next_last_ep_responses()})

no_ep = client.shows.get_last_episode(show=123, extended=True)
ep = client.shows.get_last_episode(show=123, extended=True)
Expand Down
28 changes: 10 additions & 18 deletions tests/test_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
from dataclasses import asdict

import pytest
from tests.test_data.countries import COUNTRIES
from tests.test_data.oauth import OAUTH_GET_TOKEN
from tests.utils import MockRequests
from tests.utils import MockRequests, mk_mock_client
from trakt import Trakt, TraktCredentials
from trakt.core.components import DefaultHttpComponent
from trakt.core.exceptions import ClientError
Expand All @@ -17,23 +16,16 @@

def test_executor():
response = [{"name": "Australia", "code": "au"}]
http = lambda client: DefaultHttpComponent(
client, requests_dependency=MockRequests({".*": [response, 200]})
)
client = mk_mock_client({".*": [response, 200]})

client = Trakt("", "", http_component=http)
assert len(client.request("countries", type="shows")) == len(response)
assert len(client.request("countries.get_countries", type="shows")) == len(response)

assert client.request("countries", type="shows") == client.request(
"get_countries", type="shows"
)
assert client.request(
"countries.get_countries", type="shows"
) == client.get_countries(type="shows")
assert len(client.get_countries(type="shows")) == len(response)
assert [asdict(s) for s in client.get_countries(type="shows")] == response
countries = client.request("get_countries", type="shows")
assert [asdict(s) for s in countries] == response

with pytest.raises(ClientError):
client.count(type="shows")
client.request("count", type="shows")


TOKEN_REFRESH_HTTP = lambda client: DefaultHttpComponent(
Expand All @@ -48,7 +40,7 @@ def test_refresh_token_off():
credentials = TraktCredentials("access", "refresh", "scope", 100)

client = Trakt("", "", http_component=TOKEN_REFRESH_HTTP, user=credentials)
client.get_countries(type="shows")
client.countries.get_countries(type="shows")

assert client.user.refresh_token == "refresh"
assert client.user.access_token == "access"
Expand All @@ -60,15 +52,15 @@ def test_refresh_token_on():
# token is not going to expire soon (should not refresh)
expire_at = int(time.time()) + 2 * 30 * 24 * 60 * 60 # 60 days
client.set_user(TraktCredentials("access", "refresh", "scope", expire_at))
client.get_countries(type="shows")
client.countries.get_countries(type="shows")

assert client.user.refresh_token == "refresh"
assert client.user.access_token == "access"

# token is going to expire soon
expire_at = int(time.time()) + 15 * 24 * 60 * 60 # 15 days
client.set_user(TraktCredentials("access", "refresh", "scope", expire_at))
client.get_countries(type="shows")
client.countries.get_countries(type="shows")

assert client.user.refresh_token == OAUTH_GET_TOKEN["refresh_token"]
assert client.user.access_token == OAUTH_GET_TOKEN["access_token"]
Expand Down
47 changes: 12 additions & 35 deletions tests/test_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import pytest
from tests.test_data.oauth import OAUTH_GET_TOKEN, OAUTH_VERIFICATION_CODE
from tests.utils import MockRequests, MockResponse, get_mock_http_component
from tests.utils import MockResponse, get_last_req, mk_mock_client
from trakt import Trakt, TraktCredentials
from trakt.core.components.http_component import DefaultHttpComponent
from trakt.core.components.oauth import CodeResponse
from trakt.core.exceptions import NotAuthenticated, TraktTimeoutError

Expand All @@ -20,10 +19,7 @@ def test_redirect_url():


def test_get_token():
client = Trakt(
"", "", http_component=get_mock_http_component({".*": [OAUTH_GET_TOKEN, 200]})
)

client = mk_mock_client({".*": [OAUTH_GET_TOKEN, 200]})
trakt_credentials = client.oauth.get_token(code="code", redirect_uri="uri")

assert trakt_credentials.access_token == OAUTH_GET_TOKEN["access_token"]
Expand All @@ -32,11 +28,9 @@ def test_get_token():


def test_revoke_token():
client = mk_mock_client({".*": [{}, 200]}, user=None)
user = TraktCredentials("access", "refresh", "scope", 100)

http = get_mock_http_component({".*": [{}, 200]})
client = Trakt("", "", http_component=http)

assert client.user is None

with pytest.raises(NotAuthenticated):
Expand All @@ -46,43 +40,31 @@ def test_revoke_token():
assert client.user

client.oauth.revoke_token()

assert client.user is None


def test_get_verification_code():
http = get_mock_http_component({".*": [OAUTH_VERIFICATION_CODE, 200]})
client = Trakt("123", "", http_component=http)
client = mk_mock_client({".*": [OAUTH_VERIFICATION_CODE, 200]}, client_id="123")

code = client.oauth.get_verification_code()

assert code.device_code == OAUTH_VERIFICATION_CODE["device_code"]

request_data = client.http._requests.req_map["oauth/device/code"][0]

assert json.loads(request_data["data"])["client_id"] == "123"
assert json.loads(get_last_req(client.http)["data"])["client_id"] == "123"


def test_wait_for_response_success():
client = Trakt("123", "")

def pool_endpoint_responses():
yield MockResponse({}, 412)
yield MockResponse([], 412)
yield MockResponse(OAUTH_GET_TOKEN, 200)

http = DefaultHttpComponent(
client,
requests_dependency=MockRequests(
{
"device/code": [OAUTH_VERIFICATION_CODE, 200],
"device/token": pool_endpoint_responses(),
}
),
client = mk_mock_client(
{
".*device/code.*": [OAUTH_VERIFICATION_CODE, 200],
".*device/token.*": pool_endpoint_responses(),
},
user=None,
)

client.oauth.sleep = lambda t: None
client.http = http

assert client.user is None

Expand All @@ -93,17 +75,12 @@ def pool_endpoint_responses():
assert client.user.access_token == OAUTH_GET_TOKEN["access_token"]

requests_log = client.http._requests.req_map["oauth/device/token"]

assert len(requests_log) == 3


def test_wait_for_response_timeout():
client = Trakt("123", "")

client = mk_mock_client({"device/token": [{}, 412]})
client.oauth.sleep = lambda t: None
client.http = DefaultHttpComponent(
client, requests_dependency=MockRequests({"device/token": [{}, 412]})
)

code = CodeResponse(**OAUTH_VERIFICATION_CODE)

Expand Down
71 changes: 26 additions & 45 deletions trakt/api.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, List, Optional, Type, Union
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union

from trakt.core.components import DefaultHttpComponent, DefaultOauthComponent
from trakt.core.config import Config, DefaultConfig, TraktCredentials
from trakt.core.executors import Executor
from trakt.core.models import AbstractBaseModel
from trakt.core.paths import (
DEFAULT_INTERFACES,
CalendarsI,
CertificationsI,
CheckinI,
Expand Down Expand Up @@ -38,30 +39,32 @@ class TraktApi:
oauth: DefaultOauthComponent
user: Optional[TraktCredentials]

countries: CountriesI
calendars: CalendarsI
shows: ShowsI
genres: GenresI
certifications: CertificationsI
languages: LanguagesI
lists: ListsI
movies: MoviesI
checkin: CheckinI
people: PeopleI
networks: NetworksI
comments: CommentsI
search: SearchI
recommendations: RecommendationsI
scrobble: ScrobbleI
seasons: SeasonsI
episodes: EpisodesI

def __init__(
self,
client_id: str,
client_secret: str,
*,
http_component: Optional[Type[DefaultHttpComponent]] = None,
oauth_component: Optional[Type[DefaultOauthComponent]] = None,
countries_interface: Optional[Type[CountriesI]] = None,
calendars_interface: Optional[Type[CalendarsI]] = None,
shows_interface: Optional[Type[ShowsI]] = None,
genres_interface: Optional[Type[GenresI]] = None,
certifications_interface: Optional[Type[CertificationsI]] = None,
languages_interface: Optional[Type[LanguagesI]] = None,
lists_interface: Optional[Type[ListsI]] = None,
movies_interface: Optional[Type[MoviesI]] = None,
checkin_interface: Optional[Type[CheckinI]] = None,
people_interface: Optional[Type[PeopleI]] = None,
networks_interface: Optional[Type[NetworksI]] = None,
comments_interface: Optional[Type[CommentsI]] = None,
search_interface: Optional[Type[SearchI]] = None,
recommendations_interface: Optional[Type[RecommendationsI]] = None,
scrobble_interface: Optional[Type[ScrobbleI]] = None,
seasons_interface: Optional[Type[SeasonsI]] = None,
episodes_interface: Optional[Type[EpisodesI]] = None,
interfaces: Dict[str, Type[SuiteInterface]] = None,
user: Optional[TraktCredentials] = None,
auto_refresh_token: bool = False,
**config: str
Expand All @@ -82,27 +85,11 @@ def __init__(

self.http = (http_component or DefaultHttpComponent)(self)
self.oauth = (oauth_component or DefaultOauthComponent)(self)
self.countries = (countries_interface or CountriesI)(self, Executor)
self.calendars = (calendars_interface or CalendarsI)(self, Executor)
self.shows = (shows_interface or ShowsI)(self, Executor)
self.genres = (genres_interface or GenresI)(self, Executor)
self.certifications = (certifications_interface or CertificationsI)(
self, Executor
)
self.languages = (languages_interface or LanguagesI)(self, Executor)
self.lists = (lists_interface or ListsI)(self, Executor)
self.movies = (movies_interface or MoviesI)(self, Executor)
self.checkin = (checkin_interface or CheckinI)(self, Executor)
self.people = (people_interface or PeopleI)(self, Executor)
self.networks = (networks_interface or NetworksI)(self, Executor)
self.comments = (comments_interface or CommentsI)(self, Executor)
self.search = (search_interface or SearchI)(self, Executor)
self.recommendations = (recommendations_interface or RecommendationsI)(
self, Executor
)
self.scrobble = (scrobble_interface or ScrobbleI)(self, Executor)
self.seasons = (seasons_interface or SeasonsI)(self, Executor)
self.episodes = (episodes_interface or EpisodesI)(self, Executor)

interfaces = interfaces or {}
for i_name, default in DEFAULT_INTERFACES.items():
i_obj = interfaces.get(i_name, default)(self, Executor)
setattr(self, i_name, i_obj)

def request(self, params: Union[str, List[str]], **kwargs: Any) -> Any:
if isinstance(params, str):
Expand All @@ -116,12 +103,6 @@ def request(self, params: Union[str, List[str]], **kwargs: Any) -> Any:
def set_user(self, user: TraktCredentials) -> None:
self.user = user

def __getattr__(self, item: str) -> Executor:
e = Executor(self, item)
e.install(self._get_executor_paths())

return e

def _get_executor_paths(self) -> List[SuiteInterface]:
return [
self.calendars,
Expand Down
Loading

0 comments on commit d0e3cf5

Please sign in to comment.