Пример #1
0
    def test_instance_from_environment(self):
        os.environ.update(dict(LASTFM_API_KEY="a"))

        config = Config.instance()
        expected = {
            "api_key": "a",
            "api_secret": None,
            "username": None,
            "password": None,
            "session": None,
        }
        self.assertDictEqual(expected, config.to_dict())

        os.environ.update(
            dict(
                LASTFM_API_KEY="a",
                LASTFM_API_SECRET="b",
                LASTFM_USERNAME="******",
                LASTFM_PASSWORD="******",
                LASTFM_SESSION="e",
            ))

        new_config = Config.instance()
        self.assertEqual(config, new_config)
        Config._instance = None
        expected = {
            "api_key": "a",
            "api_secret": "b",
            "username": "******",
            "password": md5("d"),
            "session": "e",
        }

        self.assertDictEqual(expected, Config.instance().to_dict())
Пример #2
0
    def prepare_params(cls, params: Dict, sign: bool, stateful: bool,
                       authenticate: bool) -> dict:
        """
        Perform common parameter tasks before sending the web request.

        * Filter out None values,
        * Set the preferred api format ``json``
        * Add the api key, session or signature based on the state flags

        :param Dict params: A dictionary of body or query string params
        :param bool sign: Sign the request with the api secret
        :param bool stateful: Add the session key to the params
        :param bool authenticate: Add the username and auth token to the params
        :rtype: Dict
        """
        cfg = Config.instance()
        params = {
            k: str(int(v is True) if isinstance(v, bool) else v)
            for k, v in params.items() if v is not None
        }
        params.update({"format": "json", "api_key": cfg.api_key})

        if authenticate:
            params.update({
                "username": cfg.username,
                "authToken": cfg.auth_token
            })

        if stateful:
            params.update({"sk": cls.get_session().key})

        if authenticate or stateful or sign:
            params.update({"api_sig": cls.sign(params)})

        return params
Пример #3
0
    def prepare_params(cls, params: Dict, sign: bool, stateful: bool,
                       authenticate: bool) -> dict:
        """
        Perform common parameter tasks before sending the web request.

        * Filter out None values,
        * Set the preferred api format ``json``
        * Add the api key, session or signature based on the state flags

        :param Dict params: A dictionary of body or query string params
        :param bool sign: Sign the request with the api secret
        :param bool stateful: Add the session key to the params
        :param bool authenticate: Add the username and auth token to the params
        :rtype: Dict
        """
        cfg = Config.instance()
        params = dict((k, str(int(v is True) if type(v) == bool else v))
                      for k, v in params.items() if v is not None)
        params.update(dict(format="json", api_key=cfg.api_key))

        if authenticate:
            params.update(dict(username=cfg.username,
                               authToken=cfg.auth_token))

        if stateful:
            params.update(dict(sk=cls.get_session().key))

        if authenticate or stateful or sign:
            params.update(dict(api_sig=cls.sign(params)))

        return params
Пример #4
0
 def setUp(self):
     super(ConfigTests, self).setUp()
     self.config = Config.instance()
     Config._instance = None
     for k in self.keys:
         try:
             del os.environ["LASTFM_{}".format(k.upper())]
         except KeyError:
             pass
Пример #5
0
 def setUp(self):
     super().setUp()
     self.config = Config.instance()
     Config._instance = None
     for k in self.keys:
         try:
             del os.environ[f"LASTFM_{k.upper()}"]
         except KeyError:
             pass
Пример #6
0
    def get_session(cls) -> "AuthSession":  # type: ignore
        """
        Return the session from configuration or attempt to authenticate the
        configuration user.

        :rtype: :class:`~pydrag.models.auth.AuthSession`
        """
        cfg = Config.instance()
        if not cfg.session:
            from pydrag.models.auth import AuthSession

            cfg.session = AuthSession.authenticate()
        return cfg.session
Пример #7
0
    def test_instance_from_arguments(self):
        config = Config.instance("key")
        expected = {
            "api_key": "key",
            "api_secret": None,
            "password": None,
            "session": None,
            "username": None,
        }
        self.assertDictEqual(expected, config.to_dict())
        self.assertEqual(config, Config.instance())

        new_config = Config.instance("a", "b", "c", "d", "e")
        expected = {
            "api_key": "a",
            "api_secret": "b",
            "username": "******",
            "password": md5("d"),
            "session": "e",
        }
        self.assertDictEqual(expected, new_config.to_dict())
        self.assertEqual(new_config, Config._instance)
        self.assertNotEqual(config, Config._instance)
Пример #8
0
    def sign(params: Dict) -> str:
        """
        Last.fm signing formula for webservice calls. Exclude format, sort
        params, append the api secret key and hash the params string.

        :param Dict params:
        :rtype: str
        """
        keys = sorted(params.keys())
        keys.remove("format")

        signature = [str(k) + str(params[k]) for k in keys if params.get(k)]
        signature.append(str(Config.instance().api_secret))
        return utils.md5("".join(signature))  # type: ignore
Пример #9
0
    def _perform(
        cls,
        method: str,
        bind: Type[BaseModel],
        flatten: Optional[str],
        params: Dict,
        sign: bool,
        stateful: bool,
        authenticate: bool,
    ):
        """
        Orchestrate the request, error handling and response deserialization.

        :param str method: Http method POST/GET
        :param bind: Class type to construct from the api response.
        :type bind: :class:`~pydrag.models.common.BaseModel`
        :param str flatten: A dot separated string used to flatten nested
        :param Dict params: A dictionary of body or query string params
        :param bool sign: Sign the request with the api secret
        :param bool stateful: Requires a session
        :param bool authenticate: Perform an authentication request
        :rtype: :class:`~pydrag.models.common.BaseModel`
        """
        data: Dict = {}
        query: Dict = {}
        if method == "GET":
            query = cls.prepare_params(params, sign, stateful, authenticate)
        else:
            data = cls.prepare_params(params, sign, stateful, authenticate)

        cfg = Config.instance()
        response = request(method=method,
                           url=cfg.api_url,
                           data=data,
                           params=query)
        response.raise_for_status()
        body = response.json(object_pairs_hook=pythonic_variables)
        cls.raise_for_error(body)
        obj = cls.bind_data(bind, body, flatten)
        obj.params = params
        return obj
Пример #10
0
    def test_instance_raises_exception(self):
        with self.assertRaises(AssertionError) as cm:
            Config.instance()

        self.assertEqual("Provide a valid last.fm api key.", str(cm.exception))
Пример #11
0
 def auth_url(self):
     return Config.auth_url.format(self.token, Config.instance().api_key)
Пример #12
0
 def setUp(self):
     self.maxDiff = None
     Config.instance().session = None
     super(MethodTestCase, self).setUp()
Пример #13
0
import json
import os
import re
from unittest import TestCase

import vcr

from pydrag.models.common import Config

try:
    config = Config.instance()
except AssertionError:
    Config.instance(api_key="key")

where_am_i = os.path.dirname(os.path.realpath(__file__))
fixtures_dir = os.path.join(where_am_i, "models", "fixtures")

censored_parameters = [
    ("token", "USER_TOKEN"),
    ("sk", "USER_SESSION"),
    ("api_sig", "API_SIG"),
    ("api_key", "LAST_FM_API_KEY"),
    ("authToken", "USER_AUTH_TOKEN"),
]


def censor_response(response):
    body = response["body"]["string"]
    body = re.sub(b"[A-Za-z0-9-_]{32}", b"CENSORED", body)
    response["body"]["string"] = body
    return response