class ApiKeyAuthRequestTestCase(RequestTestCase):
    def setUp(self):
        self.api_key = "dsfjhasdfknasdfhas"
        self.req_obj = ApiKeyAuthRequest(self.api_key)

    def test_correct_url_hit(self):
        with mock.patch('requests.put') as mock_put:
            self.req_obj.execute("http://testserver")
            mock_put.assert_called_with("http://testserver/api_auth",
                                        headers=mock.ANY,
                                        data=mock.ANY)

    def test_correct_data_sent(self):
        with mock.patch('requests.put') as mock_put:
            self.req_obj.execute("http://testserver")
            expected_data = {"api_key": self.api_key}
            self.assert_data(mock_put, expected_data)
class ApiKeyAuthRequestTestCase(RequestTestCase):
    def setUp(self):
        self.api_key = "dsfjhasdfknasdfhas"
        self.req_obj = ApiKeyAuthRequest(self.api_key)

    def test_correct_url_hit(self):
        with mock.patch('requests.put') as mock_put:
            self.req_obj.execute("http://testserver")
            mock_put.assert_called_with("http://testserver/api_auth",
                                        headers=mock.ANY,
                                        data=mock.ANY)

    def test_correct_data_sent(self):
        with mock.patch('requests.put') as mock_put:
            self.req_obj.execute("http://testserver")
            expected_data = {
                "api_key": self.api_key
            }
            self.assert_data(mock_put, expected_data)
Ejemplo n.º 3
0
class Client(object):
    """The interface to the Kazoo API

    This class should be initialized either with a username, password and
    account name combination, or with an API key. Once you have initialized
    the client you will need to call :meth:`authenticate()` before you can
    begin making API calls. ::

        >>>import kazoo
        >>>client = kazoo.Client(api_key="sdfasdfas")
        >>>client.authenticate()

    You can also initialize with a username and password combination: ::

        >>>client = kazoo.Client(username="******", password="******", account_name="my_account_name")
        >>>client.authenticate()

    API calls which require data take it in the form of a required argument
    called 'data' which is the last argument to the method. For example ::

        >>>client.update_account(acct_id, {"name": "somename", "realm":"superfunrealm"})

    Dictionaries and lists will automatically be converted to their appropriate
    representation so you can do things like: ::

        >>>client.update_callflow(acct_id, callflow_id, {"flow":{"module":"somemodule"}})

    Invalid data will result in an exception explaining the problem.

    The server response is returned from each method as a python dictionary of
    the returned JSON object, for example: ::

        >>>client.get_account(acct_id)
        {u'auth_token': u'abc437d000007d0454cc984f6f09daf3',
         u'data': {u'billing_mode': u'normal',
          u'caller_id': {},
          u'caller_id_options': {},
          u'id': u'c4f64412ad0057222c0009a3e7da011',
          u'media': {u'bypass_media': u'auto'},
          u'music_on_hold': {},
          u'name': u'test3',
          u'notifications': {},
          u'realm': u'4c8050.sip.2600hz.com',
          u'superduper_admin': False,
          u'timezone': u'America/Los_Angeles',
          u'wnm_allow_additions': False},
         u'request_id': u'ea6441422fb85000ad21db4f1e2326c1',
         u'revision': u'3-c16dd0a629fe1da0000e1e7b3e5fb35a',
         u'status': u'success'}

    For each resource exposed by the kazoo api there are corresponding methods
    on the client. For example, for the 'callflows' resource the
    correspondence is as follows. ::

        GET /accounts/{account_id}/callflows -> client.get_callflows(acct_id)
        GET /accounts/{account_id}/callflows/{callflow_id} -> client.get_callflow(acct_id, callflow_id)
        PUT /accounts/{account_id}/callflows/ -> client.create_callflow(acct_id, data)
        POST /account/{account_id}/callflows/{callflow_id} -> client.update_callflow(acct_id, data)
        DELETE /account/{account_id}/callflows/{callflow_id} -> client.delete_callflow(acct_id, callflow_id)

    Some resources do not have all methods available, in which case they are
    not present on the client.

    There are also some resources which don't quite fit this paradigm, they are: ::

        GET /accounts/{account_id}/media -> client.get_all_media(acct_id)
        GET /accounts/{account_id}/children -> client.get_account_children(acct_id)
        GET /accounts/{account_id}/descendants -> client.get_account_descendants(acct_id)
        GET /accounts/{account_id}/devices/status -> client.get_all_devices_status(acct_id)
        GET /accounts/{account_id}/servers/{server_id}/deployment -> client.get_deployment(acct_id, server_id)
        GET /accounts/{account_id}/users/hotdesk -> client.get_hotdesk(acct_id)

    """
    __metaclass__ = RestClientMetaClass
    BASE_URL = "http://api.2600hz.com:8000/v1"

    _accounts_resource = RestResource(
        "account",
        "/accounts/{account_id}",
        exclude_methods=["list", "delete", "create"],
        extra_views=[{
            "name": "get_account_children",
            "path": "children",
            "scope": "object"
        }, {
            "name": "get_account_descendants",
            "path": "descendants",
            "scope": "object"
        }])
    _callflow_resource = RestResource(
        "callflow", "/accounts/{account_id}/callflows/{callflow_id}")
    _conference_resource = RestResource(
        "conference", "/accounts/{account_id}/conferences/{conference_id}")
    _device_resource = RestResource(
        "device",
        "/accounts/{account_id}/devices/{device_id}",
        extra_views=[{
            "name": "get_all_devices_status",
            "path": "status"
        }])
    _directories_resource = RestResource(
        "directory",
        "/accounts/{account_id}/directories/{directory_id}",
        plural_name="directories")
    _global_resources = RestResource(
        "global_resource",
        "/accounts/{account_id}/global_resources/{resource_id}")
    _limits_resource = RestResource("limit",
                                    "/accounts/{account_id}/limits/{ignored}",
                                    methods=["list"])
    _local_resources_resource = RestResource(
        "local_resource",
        "/accounts/{account_id}/local_resources/{resource_id}")
    _media_resource = RestResource("media",
                                   "/accounts/{account_id}/media/{media_id}",
                                   plural_name="media",
                                   method_names={"list": "get_all_media"})
    _menus_resource = RestResource("menu",
                                   "/accounts/{account_id}/menus/{menu_id}")
    _phone_number_resource = RestResource(
        "phone_number",
        "/accounts/{account_id}/phone_numbers/{phone_number}",
        methods=["list", "update", "delete"],
        extra_views=[{
            "name": "activate_phone_number",
            "path": "activate",
            "scope": "object",
            "method": "put"
        }, {
            "name": "reserve_phone_number",
            "path": "reserve",
            "scope": "object",
            "method": "put"
        }, {
            "name": "add_port_in_number",
            "path": "port",
            "scope": "object",
            "method": "put"
        }])
    _queues_resource = RestResource(
        "queue", "/accounts/{account_id}/queues/{queue_id}")
    _server_resource = RestResource(
        "server",
        "/accounts/{account_id}/servers/{server_id}",
        methods=["list"],
        extra_views=[{
            "name": "get_deployment",
            "path": "deployment",
            "scope": "object"
        }, {
            "name": "create_deployment",
            "path": "deployment",
            "scope": "object",
            "method": "put"
        }, {
            "name": "get_server_log",
            "path": "log"
        }])
    _temporal_rules_resource = RestResource(
        "temporal_rule", "/accounts/{account_id}/temporal_rules/{rule_id}")
    _users_resource = RestResource("user",
                                   "/accounts/{account_id}/users/{user_id}",
                                   extra_views=[{
                                       "name": "get_hotdesk",
                                       "path": "hotdesks"
                                   }])
    _vmbox_resource = RestResource("voicemail_box",
                                   "/accounts/{account_id}/vmboxes/{vmbox_id}",
                                   plural_name="voicemail_boxes")
    _phone_number_docs_resource = RestResource(
        "phone_number_doc",
        "/accounts/{account_id}/phone_numbers/{phone_number}/docs/{filename}",
        methods=["delete"],
    )

    def __init__(self,
                 api_key=None,
                 password=None,
                 account_name=None,
                 username=None):
        if not api_key and not password:
            raise RuntimeError("You must pass either an api_key or an "
                               "account name/password pair")

        if password or account_name or username:
            if not (password and account_name and username):
                raise RuntimeError("If using account name/password "
                                   "authentication then you must specify "
                                   "password, userame and account_name "
                                   "arguments")
            self.auth_request = UsernamePasswordAuthRequest(
                username, password, account_name)
        else:
            self.auth_request = ApiKeyAuthRequest(api_key)

        self.api_key = api_key
        self._authenticated = False
        self.auth_token = None

    def authenticate(self):
        """Call this before making other api calls to fetch an auth token
        which will be automatically used for all further requests
        """
        if not self._authenticated:
            self.auth_data = self.auth_request.execute(self.BASE_URL)
            self.auth_token = self.auth_data["auth_token"]
            self._authenticated = True
        return self.auth_token

    def _execute_request(self, request, **kwargs):
        if request.auth_required:
            kwargs["token"] = self.auth_token
        return request.execute(self.BASE_URL, **kwargs)

    def search_phone_numbers(self, prefix, quantity=10):
        request = KazooRequest("/phone_numbers",
                               get_params={
                                   "prefix": prefix,
                                   "quantity": quantity
                               })
        return self._execute_request(request)

    def create_phone_number(self, acct_id, phone_number):
        request = KazooRequest(
            "/accounts/{account_id}/phone_numbers/{phone_number}",
            method="put")
        return self._execute_request(request,
                                     account_id=acct_id,
                                     phone_number=phone_number)

    def upload_phone_number_file(self, acct_id, phone_number, filename,
                                 file_obj):
        """Uploads a file like object as part of a phone numbers documents"""
        request = KazooRequest(
            "/accounts/{account_id}/phone_numbers/{phone_number}",
            method="post")
        return self._execute_request(request, files={filename: file_obj})
Ejemplo n.º 4
0
class Client(object):
    """The interface to the Kazoo API

    This class should be initialized either with a username, password and
    account name combination, or with an API key. Once you have initialized
    the client you will need to call :meth:`authenticate()` before you can
    begin making API calls. ::

        >>>import kazoo
        >>>client = kazoo.Client(api_key="sdfasdfas")
        >>>client.authenticate()

    You can also initialize with a username and password combination: ::

        >>>client = kazoo.Client(username="******", password="******", account_name="my_account_name")
        >>>client.authenticate()

    The default api url is: 'http://api.2600hz.com:8000/v1'.  You can override this
    by supplying an extra argument, 'base_url' to kazoo.Client().

    Example of overriding 'base_url'::

        >>>client = kazoo.Client(base_url='http://api.example.com:8000/v1',
                                 api_key="sdfasdfas")

    API calls which require data take it in the form of a required argument
    called 'data' which is the last argument to the method. For example ::

        >>>client.update_account(acct_id, {"name": "somename", "realm":"superfunrealm"})

    Dictionaries and lists will automatically be converted to their appropriate
    representation so you can do things like: ::

        >>>client.update_callflow(acct_id, callflow_id, {"flow":{"module":"somemodule"}})

    Invalid data will result in an exception explaining the problem.

    The server response is returned from each method as a python dictionary of
    the returned JSON object, for example: ::

        >>>client.get_account(acct_id)
        {u'auth_token': u'abc437d000007d0454cc984f6f09daf3',
         u'data': {u'billing_mode': u'normal',
          u'caller_id': {},
          u'caller_id_options': {},
          u'id': u'c4f64412ad0057222c0009a3e7da011',
          u'media': {u'bypass_media': u'auto'},
          u'music_on_hold': {},
          u'name': u'test3',
          u'notifications': {},
          u'realm': u'4c8050.sip.2600hz.com',
          u'superduper_admin': False,
          u'timezone': u'America/Los_Angeles',
          u'wnm_allow_additions': False},
         u'request_id': u'ea6441422fb85000ad21db4f1e2326c1',
         u'revision': u'3-c16dd0a629fe1da0000e1e7b3e5fb35a',
         u'status': u'success'}

    For each resource exposed by the kazoo api there are corresponding methods
    on the client. For example, for the 'callflows' resource the
    correspondence is as follows. ::

        GET /accounts/{account_id}/callflows -> client.get_callflows(acct_id)
        GET /accounts/{account_id}/callflows/{callflow_id} -> client.get_callflow(acct_id, callflow_id)
        PUT /accounts/{account_id}/callflows/ -> client.create_callflow(acct_id, data)
        POST /account/{account_id}/callflows/{callflow_id} -> client.update_callflow(acct_id, data)
        DELETE /account/{account_id}/callflows/{callflow_id} -> client.delete_callflow(acct_id, callflow_id)

    Some resources do not have all methods available, in which case they are
    not present on the client.

    There are also some resources which don't quite fit this paradigm, they are: ::

        GET /accounts/{account_id}/media -> client.get_all_media(acct_id)
        GET /accounts/{account_id}/children -> client.get_account_children(acct_id)
        GET /accounts/{account_id}/descendants -> client.get_account_descendants(acct_id)
        GET /accounts/{account_id}/devices/status -> client.get_all_devices_status(acct_id)
        GET /accounts/{account_id}/servers/{server_id}/deployment -> client.get_deployment(acct_id, server_id)
        GET /accounts/{account_id}/users/hotdesk -> client.get_hotdesk(acct_id)

    """
    __metaclass__ = RestClientMetaClass
    base_url = "http://api.2600hz.com:8000/v1"

    _accounts_resource = RestResource("account",
                                      "/accounts/{account_id}",
                                      exclude_methods=[],
                                      extra_views=[
                                          {"name": "get_account_children",
                                           "path": "children",
                                           "scope": "object"},
                                          {"name": "get_account_descendants",
                                           "path": "descendants",
                                           "scope": "object"}])

    _callflow_resource = RestResource(
        "callflow",
        "/accounts/{account_id}/callflows/{callflow_id}")

    _conference_resource = RestResource(
        "conference",
        "/accounts/{account_id}/conferences/{conference_id}")

    _device_resource = RestResource(
        "device",
        "/accounts/{account_id}/devices/{device_id}",
        extra_views=[{"name": "get_all_devices_status", "path": "status"}])

    _directories_resource = RestResource(
        "directory",
        "/accounts/{account_id}/directories/{directory_id}",
        plural_name="directories")

    _global_resources = RestResource(
        "global_resource",
        "/accounts/{account_id}/global_resources/{resource_id}")

    _groups_resource = RestResource("group",
                                   "/accounts/{account_id}/groups/{group_id}")

    _limits_resource = RestResource("limit",
                                    "/accounts/{account_id}/limits/{ignored}",
                                    methods=["list"])

    _local_resources_resource = RestResource(
        "local_resource",
        "/accounts/{account_id}/local_resources/{resource_id}")


    _media_resource = RestResource("media",
                                   "/accounts/{account_id}/media/{media_id}",
                                   plural_name="media",
                                   method_names={
                                       "list": "get_all_media"
                                   })

    _menus_resource = RestResource("menu",
                                   "/accounts/{account_id}/menus/{menu_id}")

    _phone_number_resource = RestResource(
        "phone_number",
        "/accounts/{account_id}/phone_numbers/{phone_number}",
        methods=["list", "update", "delete"],
        extra_views=[
            {"name":"activate_phone_number",
             "path": "activate",
             "scope": "object",
             "method": "put"},
            {"name": "reserve_phone_number",
             "path": "reserve",
             "scope": "object",
             "method": "put"},
            {"name": "add_port_in_number",
             "path": "port",
             "scope": "object",
             "method": "put"}])

    _queues_resource = RestResource("queue",
                                    "/accounts/{account_id}/queues/{queue_id}")

    _rates_resource = RestResource("rates",
                                    "/accounts/{account_id}/rates/{rate_id}")

    _server_resource = RestResource(
        "server",
        "/accounts/{account_id}/servers/{server_id}",
        methods=["list"],
        extra_views=[
            {"name": "get_deployment",
             "path": "deployment",
             "scope": "object"},
            {"name": "create_deployment",
             "path": "deployment",
             "scope": "object",
             "method": "put"},
            {"name": "get_server_log", "path": "log"}
        ])

    _temporal_rules_resource = RestResource(
        "temporal_rule",
        "/accounts/{account_id}/temporal_rules/{rule_id}")

    _users_resource = RestResource(
        "user",
        "/accounts/{account_id}/users/{user_id}",
        extra_views=[{"name": "get_hotdesk", "path": "hotdesks"}])

    _vmbox_resource = RestResource(
        "voicemail_box",
        "/accounts/{account_id}/vmboxes/{vmbox_id}",
        plural_name="voicemail_boxes")

    _phone_number_docs_resource = RestResource(
        "phone_number_doc",
        "/accounts/{account_id}/phone_numbers/{phone_number}/docs/{filename}",
        methods=["delete"],
    )

    _webhook_resource = RestResource(
        "webhook",
        "/accounts/{account_id}/webhooks/{webhook_id}")

    def __init__(self, api_key=None, password=None, account_name=None,
                 username=None, base_url=None):
        if not api_key and not password:
            raise RuntimeError("You must pass either an api_key or an "
                               "account name/password pair")

        if base_url is not None:
            self.base_url = base_url

        if password or account_name or username:
            if not (password and account_name and username):
                raise RuntimeError("If using account name/password "
                                   "authentication then you must specify "
                                   "password, userame and account_name "
                                   "arguments")
            self.auth_request = UsernamePasswordAuthRequest(username,
                                                            password,
                                                            account_name)
        else:
            self.auth_request = ApiKeyAuthRequest(api_key)

        self.api_key = api_key
        self._authenticated = False
        self.auth_token = None

    def authenticate(self):
        """Call this before making other api calls to fetch an auth token
        which will be automatically used for all further requests
        """
        if not self._authenticated:
            self.auth_data = self.auth_request.execute(self.base_url)
            self.auth_token = self.auth_data["auth_token"]
            self._authenticated = True
        return self.auth_token

    def _execute_request(self, request, **kwargs):
        from exceptions import KazooApiAuthenticationError

        if request.auth_required:
            kwargs["token"] = self.auth_token

        try:
            return request.execute(self.base_url, **kwargs)
        except KazooApiAuthenticationError as e:
            logger.error('Kazoo authentication failed. Attempting to re-authentication and retry: {}'.format(e))
            self._authenticated = False
            self.auth_token = None
            self.authenticate()
            kwargs["token"] = self.auth_token
            return request.execute(self.base_url, **kwargs)

    def search_phone_numbers(self, prefix, quantity=10):
        request = KazooRequest("/phone_numbers", get_params={
            "prefix": prefix,
            "quantity": quantity
        })
        return self._execute_request(request)

    def create_phone_number(self, acct_id, phone_number):
        request = KazooRequest("/accounts/{account_id}/phone_numbers/{phone_number}",
                               method="put")
        return self._execute_request(request,
                                     account_id=acct_id, phone_number=phone_number)

    def get_phone_number(self, acct_id, phone_number):
        request = KazooRequest("/accounts/{account_id}/phone_numbers/{phone_number}",
                               method="get")
        return self._execute_request(request,
                                     account_id=acct_id, phone_number=phone_number)

    def upload_media_file(self, acct_id, media_id, filename, file_obj):
        """Uploads a media file like object as part of a media document"""
        request = KazooRequest("/accounts/{account_id}/media/{media_id}/raw",
                               method="post")
        return self._execute_request(request, 
                                     account_id=acct_id, 
                                     media_id=media_id,
                                     rawfiles=({filename: file_obj}))

    def upload_phone_number_file(self, acct_id, phone_number, filename, file_obj):
        """Uploads a file like object as part of a phone numbers documents"""
        request = KazooRequest("/accounts/{account_id}/phone_numbers/{phone_number}",
                               method="post")
        return self._execute_request(request, files={filename: file_obj})

    def list_devices_by_owner(self, accountId, ownerId):
        request = KazooRequest("/accounts/{account_id}/devices", get_params={"filter_owner_id": ownerId})
        request.auth_required = True

        return self._execute_request(request, account_id=accountId)

    def list_child_accounts(self, parentAccountId):
        request = KazooRequest("/accounts/{account_id}/children")
        request.auth_required = True

        return self._execute_request(request, account_id=parentAccountId)