# -*- coding: utf-8 -*-
"""Webex Teams Licenses API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type,
    dict_from_items_with_values,
)

API_ENDPOINT = 'licenses'
OBJECT_TYPE = 'license'

class LicensesAPI(object):
    """Webex Teams Licenses API.

    Wraps the Webex Teams Licenses API and exposes the API as native Python
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Initialize a new LicensesAPI object with the provided RestSession.

        Args:
            session(RestSession): The RESTful session object to be used for
                API calls to the Webex Teams service.

        Raises:
            TypeError: If the input object is not a dictionary or string.

        """
        check_type(session, RestSession, may_be_none=False)

        super(LicensesAPI, self).__init__()

        self._session = session
        self._object_factory = object_factory

    @generator_container
    def list(self, orgId=None, **request_parameters):
        """List all licenses for a given organization.

        If no orgId is specified, the default is the organization of the
        authenticated user.

        Args:
            orgId(basestring): Specify the organization, by ID.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            GeneratorContainer: A GeneratorContainer which, when iterated,
            yields the licenses returned by the Webex Teams query.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(orgId, basestring)

        params = dict_from_items_with_values(
            request_parameters,
            orgId=orgId,
        )

        # API request - get items
        items = self._session.get_items(API_ENDPOINT, params=params)

        # Yield license objects created from the returned JSON objects
        for item in items:
            yield self._object_factory(OBJECT_TYPE, item)

    def get(self, licenseId):
        """Get the details of a License, by ID.

        Args:
            licenseId(basestring): The ID of the License to be retrieved.

        Returns:
            License: A License object with the details of the requested
            License.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(licenseId, basestring, may_be_none=False)

        # API request
        json_data = self._session.get(API_ENDPOINT + '/' + licenseId)

        # Return a license object created from the returned JSON object
        return self._object_factory(OBJECT_TYPE, json_data)


# -*- coding: utf-8 -*-
"""Webex Teams Webhook data model.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from webexteamssdk.utils import WebexTeamsDateTime

class WebhookBasicPropertiesMixin(object):
    """Webhook basic properties."""

    @property
    def id(self):
        """Webhook ID."""
        return self._json_data.get('id')

    @property
    def name(self):
        """A user-friendly name for this webhook."""
        return self._json_data.get('name')

    @property
    def targetUrl(self):
        """The URL that receives POST requests for each event."""
        return self._json_data.get('targetUrl')

    @property
    def resource(self):
        """The resource type for the webhook."""
        return self._json_data.get('resource')

    @property
    def event(self):
        """The event type for the webhook."""
        return self._json_data.get('event')

    @property
    def filter(self):
        """The filter that defines the webhook scope."""
        return self._json_data.get('filter')

    @property
    def secret(self):
        """Secret used to generate payload signature."""
        return self._json_data.get('secret')

    @property
    def orgId(self):
        """The ID of the organization that owns the webhook."""
        return self._json_data.get('orgId')

    @property
    def createdBy(self):
        """The ID of the person that added the webhook."""
        return self._json_data.get('createdBy')

    @property
    def appId(self):
        """Identifies the application that added the webhook."""
        return self._json_data.get('appId')

    @property
    def ownedBy(self):
        """Indicates if the webhook is owned by the `org` or the `creator`.

        Webhooks owned by the creator can only receive events that are
        accessible to the creator of the webhook. Those owned by the
        organization will receive events that are visible to anyone in the
        organization.

        """
        return self._json_data.get('ownedBy')

    @property
    def status(self):
        """Indicates if the webhook is active.

        A webhook that cannot reach your URL is disabled.

        """
        return self._json_data.get('status')

    @property
    def created(self):
        """Creation date and time in ISO8601 format."""
        created = self._json_data.get('created')
        if created:
            return WebexTeamsDateTime.strptime(created)
        else:
            return None


# -*- coding: utf-8 -*-
"""GeneratorContainer class makes generator functions safe for reuse.

Classes:
    GeneratorContainer: Makes generator functions sage for reuse.

Functions:
    generator_container: Function decorator for wrapping a generator function
        in a GeneratorContainer.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *
import functools
import inspect
from itertools import islice
import sys

class GeneratorContainer(object):
    """Store a generator function call, making it for safe reuse.

    Return a fresh generator every time __iter__() is called on the container
    object.

    """

    def __init__(self, generator_function, *args, **kwargs):
        """Init a new GeneratorContainer.

        Args:
            generator_function(func): The generator function.
            *args: The arguments passed to the generator function.
            **kwargs: The keyword arguments passed to the generator function.

        """
        if not inspect.isgeneratorfunction(generator_function):
            raise TypeError("generator_function must be a generator function.")

        self.generator_function = generator_function

        if sys.version_info[0] < 3:
            self.arguments = inspect.getcallargs(
                self.generator_function,
                *args,
                **kwargs
            )
        else:
            signature = inspect.signature(self.generator_function)
            bound_arguments = signature.bind(*args, **kwargs)
            self.arguments = bound_arguments.arguments

    def __repr__(self):
        """A string representation of this object."""
        return '<GeneratorContainer {func_name}({arguments})>'.format(
            func_name=self.generator_function.__name__,
            arguments=", ".join(
                str(key) + '=' + repr(value)
                for key, value in self.arguments.items()
            ),
        )

    def __str__(self):
        """A human-readable string representation of this object."""
        return self.__repr__()

    def new_generator(self):
        """Create a new generator object."""
        return self.generator_function(**self.arguments)

    def __iter__(self):
        """Return a fresh iterator."""
        return self.new_generator()

    def __getitem__(self, item):
        """Slice a generator container.

        This is a convenience feature that, with a minor optimization, is
        essentially syntactic sugar for:

        `itertools.islice(GeneratorContainer, start, stop, step)`

        This method attempts to optimize the Webex Teams request page size for
        slicing by setting the `max` parameter to the stop-value of the slice.
        If the sliced sequence can be returned in a single response, it will
        be. Otherwise automatic pagination will take care of returning enough
        pages for the data to be sliced.  If `max=` was already specified as a
        parameter to the generator function wrapped by the GeneratorContainer,
        this optimization will not change the value.

        Args:
            item(slice): A slice object specifying the start, stop and step.

        Returns:
            itertools.islice: An itertools.islice object slicing the
                GeneratorContainer's wrapped generator function.

        """
        if isinstance(item, slice):
            arguments = self.arguments.copy()
            arguments.setdefault('max', item.stop)
            return islice(
                self.generator_function(**arguments),
                item.start,
                item.stop,
                item.step,
            )
        else:
            raise IndexError("GeneratorContainers support slicing only. "
                             "Indexing is not supported.")

def generator_container(generator_function):
    """Function Decorator: Containerize calls to a generator function.

    Args:
        generator_function(func): The generator function being containerized.

    Returns:
        func: A wrapper function that containerizes the calls to the generator
            function.

    """

    @functools.wraps(generator_function)
    def generator_container_wrapper(*args, **kwargs):
        """Store a generator call in a container and return the container.

        Args:
            *args: The arguments passed to the generator function.
            **kwargs: The keyword arguments passed to the generator function.

        Returns:
            GeneratorContainer: A container wrapping the call to the generator.

        """
        return GeneratorContainer(generator_function, *args, **kwargs)

    return generator_container_wrapper


#!/usr/bin/env python
#  -*- coding: utf-8 -*-
"""A simple bot script, built on Flask.

This sample script leverages the Flask web service micro-framework
(see http://flask.pocoo.org/).  By default the web server will be reachable at
port 5000 you can change this default if desired (see `flask_app.run(...)`).

ngrok (https://ngrok.com/) can be used to tunnel traffic back to your server
if your machine sits behind a firewall.

You must create a Webex Teams webhook that points to the URL where this script
is hosted.  You can do this via the WebexTeamsAPI.webhooks.create() method.

Additional Webex Teams webhook details can be found here:
https://developer.webex.com/webhooks-explained.html

A bot must be created and pointed to this server in the My Apps section of
https://developer.webex.com.  The bot's Access Token should be added as a
'WEBEX_TEAMS_ACCESS_TOKEN' environment variable on the web server hosting this
script.

This script supports Python versions 2 and 3.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

# Use future for Python v2 and v3 compatibility
from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)
from builtins import *

__author__ = "Chris Lunsford"
__author_email__ = "chrlunsf@cisco.com"
__contributors__ = ["Brad Bester <brbester@cisco.com>"]
__copyright__ = "Copyright (c) 2016-2019 Cisco and/or its affiliates."
__license__ = "MIT"

from flask import Flask, request
import requests

from webexteamssdk import WebexTeamsAPI, Webhook

# Module constants
CAT_FACTS_URL = 'https://catfact.ninja/fact'

# Initialize the environment
# Create the web application instance
flask_app = Flask(__name__)
# Create the Webex Teams API connection object
api = WebexTeamsAPI()

# Helper functions
def get_catfact():
    """Get a cat fact from catfact.ninja and return it as a string.

    Functions for Soundhound, Google, IBM Watson, or other APIs can be added
    to create the desired functionality into this bot.

    """
    response = requests.get(CAT_FACTS_URL, verify=False)
    response.raise_for_status()
    json_data = response.json()
    return json_data['fact']

# Core bot functionality
# Your Webex Teams webhook should point to http://<serverip>:5000/events
@flask_app.route('/events', methods=['GET', 'POST'])
def webex_teams_webhook_events():
    """Processes incoming requests to the '/events' URI."""
    if request.method == 'GET':
        return ("""<!DOCTYPE html>

                           <meta charset="UTF-8">
                           <title>Webex Teams Bot served via Flask</title>

                   Your Flask web server is up and running!

                   Here is a nice Cat Fact for you:

{}

                """.format(get_catfact()))
    elif request.method == 'POST':
        """Respond to inbound webhook JSON HTTP POST from Webex Teams."""

        # Get the POST data sent from Webex Teams
        json_data = request.json
        print("\n")
        print("WEBHOOK POST RECEIVED:")
        print(json_data)
        print("\n")

        # Create a Webhook object from the JSON data
        webhook_obj = Webhook(json_data)
        # Get the room details
        room = api.rooms.get(webhook_obj.data.roomId)
        # Get the message details
        message = api.messages.get(webhook_obj.data.id)
        # Get the sender's details
        person = api.people.get(message.personId)

        print("NEW MESSAGE IN ROOM '{}'".format(room.title))
        print("FROM '{}'".format(person.displayName))
        print("MESSAGE '{}'\n".format(message.text))

        # This is a VERY IMPORTANT loop prevention control step.
        # If you respond to all messages...  You will respond to the messages
        # that the bot posts and thereby create a loop condition.
        me = api.people.me()
        if message.personId == me.id:
            # Message was sent by me (bot); do not respond.
            return 'OK'

        else:
            # Message was sent by someone else; parse message and respond.
            if "/CAT" in message.text:
                print("FOUND '/CAT'")
                # Get a cat fact
                cat_fact = get_catfact()
                print("SENDING CAT FACT '{}'".format(cat_fact))
                # Post the fact to the room where the request was received
                api.messages.create(room.id, text=cat_fact)
            return 'OK'

if __name__ == '__main__':
    # Start the Flask web server
    flask_app.run(host='0.0.0.0', port=5000)


# -*- coding: utf-8 -*-
"""Package environment variables.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

import os
import warnings

from .config import (
    ACCESS_TOKEN_ENVIRONMENT_VARIABLE,
    LEGACY_ACCESS_TOKEN_ENVIRONMENT_VARIABLES,
)

# Helper Functions
def _get_access_token():
    """Attempt to get the access token from the environment.

    Try using the current and legacy environment variables. If the access token
    is found in a legacy environment variable, raise a deprecation warning.

    Returns:
        The access token found in the environment (str), or None.
    """
    access_token = os.environ.get(ACCESS_TOKEN_ENVIRONMENT_VARIABLE)
    if access_token:
        return access_token

    else:
        for access_token_variable in LEGACY_ACCESS_TOKEN_ENVIRONMENT_VARIABLES:
            access_token = os.environ.get(access_token_variable)
            if access_token:
                env_var_deprecation_warning = PendingDeprecationWarning(
                    "Use of the `{legacy}` environment variable will be "
                    "deprecated in the future.  Please update your "
                    "environment(s) to use the new `{new}` environment "
                    "variable.".format(
                        legacy=access_token,
                        new=ACCESS_TOKEN_ENVIRONMENT_VARIABLE,
                    )
                )
                warnings.warn(env_var_deprecation_warning)
                return access_token

# Package Environment Variables
WEBEX_TEAMS_ACCESS_TOKEN = _get_access_token()


# -*- coding: utf-8 -*-
"""Webex Teams Webhooks API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type,
    dict_from_items_with_values,
)

API_ENDPOINT = 'webhooks'
OBJECT_TYPE = 'webhook'

class WebhooksAPI(object):
    """Webex Teams Webhooks API.

    Wraps the Webex Teams Webhooks API and exposes the API as native Python
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Initialize a new WebhooksAPI object with the provided RestSession.

        Args:
            session(RestSession): The RESTful session object to be used for
                API calls to the Webex Teams service.

        Raises:
            TypeError: If the parameter types are incorrect.

        """
        check_type(session, RestSession)

        super(WebhooksAPI, self).__init__()

        self._session = session
        self._object_factory = object_factory

    @generator_container
    def list(self, max=None, **request_parameters):
        """List all of the authenticated user's webhooks.

        This method supports Webex Teams's implementation of RFC5988 Web
        Linking to provide pagination support.  It returns a generator
        container that incrementally yields all webhooks returned by the
        query.  The generator will automatically request additional 'pages' of
        responses from Webex as needed until all responses have been returned.
        The container makes the generator safe for reuse.  A new API call will
        be made, using the same parameters that were specified when the
        generator was created, every time a new iterator is requested from the
        container.

        Args:
            max(int): Limit the maximum number of items returned from the Webex
                Teams service per request.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            GeneratorContainer: A GeneratorContainer which, when iterated,
            yields the webhooks returned by the Webex Teams query.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(max, int)

        params = dict_from_items_with_values(
            request_parameters,
            max=max,
        )

        # API request - get items
        items = self._session.get_items(API_ENDPOINT, params=params)

        # Yield webhook objects created from the returned items JSON objects
        for item in items:
            yield self._object_factory(OBJECT_TYPE, item)

    def create(self, name, targetUrl, resource, event,
               filter=None, secret=None, **request_parameters):
        """Create a webhook.

        Args:
            name(basestring): A user-friendly name for this webhook.
            targetUrl(basestring): The URL that receives POST requests for
                each event.
            resource(basestring): The resource type for the webhook.
            event(basestring): The event type for the webhook.
            filter(basestring): The filter that defines the webhook scope.
            secret(basestring): The secret used to generate payload signature.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            Webhook: A Webhook object with the details of the created webhook.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(name, basestring, may_be_none=False)
        check_type(targetUrl, basestring, may_be_none=False)
        check_type(resource, basestring, may_be_none=False)
        check_type(event, basestring, may_be_none=False)
        check_type(filter, basestring)
        check_type(secret, basestring)

        post_data = dict_from_items_with_values(
            request_parameters,
            name=name,
            targetUrl=targetUrl,
            resource=resource,
            event=event,
            filter=filter,
            secret=secret,
        )

        # API request
        json_data = self._session.post(API_ENDPOINT, json=post_data)

        # Return a webhook object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def get(self, webhookId):
        """Get the details of a webhook, by ID.

        Args:
            webhookId(basestring): The ID of the webhook to be retrieved.

        Returns:
            Webhook: A Webhook object with the details of the requested
            webhook.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(webhookId, basestring, may_be_none=False)

        # API request
        json_data = self._session.get(API_ENDPOINT + '/' + webhookId)

        # Return a webhook object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def update(self, webhookId, name=None, targetUrl=None,
               **request_parameters):
        """Update a webhook, by ID.

        Args:
            webhookId(basestring): The webhook ID.
            name(basestring): A user-friendly name for this webhook.
            targetUrl(basestring): The URL that receives POST requests for
                each event.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            Webhook: A Webhook object with the updated Webex Teams webhook
                details.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(webhookId, basestring, may_be_none=False)
        check_type(name, basestring)
        check_type(targetUrl, basestring)

        put_data = dict_from_items_with_values(
            request_parameters,
            name=name,
            targetUrl=targetUrl,
        )

        # API request
        json_data = self._session.put(API_ENDPOINT + '/' + webhookId,
                                      json=put_data)

        # Return a webhook object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def delete(self, webhookId):
        """Delete a webhook, by ID.

        Args:
            webhookId(basestring): The ID of the webhook to be deleted.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(webhookId, basestring, may_be_none=False)

        # API request
        self._session.delete(API_ENDPOINT + '/' + webhookId)


# -*- coding: utf-8 -*-
"""RestSession class for creating connections to the Webex Teams APIs.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from future import standard_library
standard_library.install_aliases()

import time
import urllib.parse
import warnings
from builtins import *

import requests
from past.builtins import basestring

from .config import DEFAULT_SINGLE_REQUEST_TIMEOUT, DEFAULT_WAIT_ON_RATE_LIMIT
from .exceptions import MalformedResponse, RateLimitError, RateLimitWarning
from .response_codes import EXPECTED_RESPONSE_CODE
from .utils import (
    check_response_code, check_type, extract_and_parse_json, validate_base_url,
)

# Helper Functions
def _fix_next_url(next_url):
    """Remove max=null parameter from URL.

    Patch for Webex Teams Defect: 'next' URL returned in the Link headers of
    the responses contain an errant 'max=null' parameter, which  causes the
    next request (to this URL) to fail if the URL is requested as-is.

    This patch parses the next_url to remove the max=null parameter.

    Args:
        next_url(basestring): The 'next' URL to be parsed and cleaned.

    Returns:
        basestring: The clean URL to be used for the 'next' request.

    Raises:
        AssertionError: If the parameter types are incorrect.
        ValueError: If 'next_url' does not contain a valid API endpoint URL
            (scheme, netloc and path).

    """
    next_url = str(next_url)
    parsed_url = urllib.parse.urlparse(next_url)

    if not parsed_url.scheme or not parsed_url.netloc or not parsed_url.path:
        raise ValueError(
            "'next_url' must be a valid API endpoint URL, minimally "
            "containing a scheme, netloc and path."
        )

    if parsed_url.query:
        query_list = parsed_url.query.split('&')
        if 'max=null' in query_list:
            query_list.remove('max=null')
            warnings.warn("`max=null` still present in next-URL returned "
                          "from Webex Teams", RuntimeWarning)
        new_query = '&'.join(query_list)
        parsed_url = list(parsed_url)
        parsed_url[4] = new_query

    return urllib.parse.urlunparse(parsed_url)

# Main module interface
class RestSession(object):
    """RESTful HTTP session class for making calls to the Webex Teams APIs."""

    def __init__(self, access_token, base_url,
                 single_request_timeout=DEFAULT_SINGLE_REQUEST_TIMEOUT,
                 wait_on_rate_limit=DEFAULT_WAIT_ON_RATE_LIMIT):
        """Initialize a new RestSession object.

        Args:
            access_token(basestring): The Webex Teams access token to be used
                for this session.
            base_url(basestring): The base URL that will be suffixed onto API
                endpoint relative URLs to produce a callable absolute URL.
            single_request_timeout(int): The timeout (seconds) for a single
                HTTP REST API request.
            wait_on_rate_limit(bool): Enable or disable automatic rate-limit
                handling.

        Raises:
            TypeError: If the parameter types are incorrect.

        """
        check_type(access_token, basestring, may_be_none=False)
        check_type(base_url, basestring, may_be_none=False)
        check_type(single_request_timeout, int)
        check_type(wait_on_rate_limit, bool, may_be_none=False)

        super(RestSession, self).__init__()

        # Initialize attributes and properties
        self._base_url = str(validate_base_url(base_url))
        self._access_token = str(access_token)
        self._single_request_timeout = single_request_timeout
        self._wait_on_rate_limit = wait_on_rate_limit

        # Initialize a new `requests` session
        self._req_session = requests.session()

        # Update the headers of the `requests` session
        self.update_headers({'Authorization': 'Bearer ' + access_token,
                             'Content-type': 'application/json;charset=utf-8'})

    @property
    def base_url(self):
        """The base URL for the API endpoints."""
        return self._base_url

    @property
    def access_token(self):
        """The Webex Teams access token used for this session."""
        return self._access_token

    @property
    def single_request_timeout(self):
        """The timeout (seconds) for a single HTTP REST API request."""
        return self._single_request_timeout

    @single_request_timeout.setter
    def single_request_timeout(self, value):
        """The timeout (seconds) for a single HTTP REST API request."""
        check_type(value, int)
        assert value is None or value > 0
        self._single_request_timeout = value

    @property
    def wait_on_rate_limit(self):
        """Automatic rate-limit handling.

        This setting enables or disables automatic rate-limit handling.  When
        enabled, rate-limited requests will be automatically be retried after
        waiting `Retry-After` seconds (provided by Webex Teams in the
        rate-limit response header).

        """
        return self._wait_on_rate_limit

    @wait_on_rate_limit.setter
    def wait_on_rate_limit(self, value):
        """Enable or disable automatic rate-limit handling."""
        check_type(value, bool, may_be_none=False)
        self._wait_on_rate_limit = value

    @property
    def headers(self):
        """The HTTP headers used for requests in this session."""
        return self._req_session.headers.copy()

    def update_headers(self, headers):
        """Update the HTTP headers used for requests in this session.

        Note: Updates provided by the dictionary passed as the `headers`
        parameter to this method are merged into the session headers by adding
        new key-value pairs and/or updating the values of existing keys. The
        session headers are not replaced by the provided dictionary.

        Args:
             headers(dict): Updates to the current session headers.

        """
        check_type(headers, dict, may_be_none=False)
        self._req_session.headers.update(headers)

    def abs_url(self, url):
        """Given a relative or absolute URL; return an absolute URL.

        Args:
            url(basestring): A relative or absolute URL.

        Returns:
            str: An absolute URL.

        """
        parsed_url = urllib.parse.urlparse(url)
        if not parsed_url.scheme and not parsed_url.netloc:
            # url is a relative URL; combine with base_url
            return urllib.parse.urljoin(str(self.base_url), str(url))
        else:
            # url is already an absolute URL; return as is
            return url

    def request(self, method, url, erc, **kwargs):
        """Abstract base method for making requests to the Webex Teams APIs.

        This base method:
            * Expands the API endpoint URL to an absolute URL
            * Makes the actual HTTP request to the API endpoint
            * Provides support for Webex Teams rate-limiting
            * Inspects response codes and raises exceptions as appropriate

        Args:
            method(basestring): The request-method type ('GET', 'POST', etc.).
            url(basestring): The URL of the API endpoint to be called.
            erc(int): The expected response code that should be returned by the
                Webex Teams API endpoint to indicate success.
            **kwargs: Passed on to the requests package.

        Raises:
            ApiError: If anything other than the expected response code is
                returned by the Webex Teams API endpoint.

        """
        # Ensure the url is an absolute URL
        abs_url = self.abs_url(url)

        # Update request kwargs with session defaults
        kwargs.setdefault('timeout', self.single_request_timeout)

        while True:
            # Make the HTTP request to the API endpoint
            response = self._req_session.request(method, abs_url, **kwargs)

            try:
                # Check the response code for error conditions
                check_response_code(response, erc)
            except RateLimitError as e:
                # Catch rate-limit errors
                # Wait and retry if automatic rate-limit handling is enabled
                if self.wait_on_rate_limit:
                    warnings.warn(RateLimitWarning(response))
                    time.sleep(e.retry_after)
                    continue
                else:
                    # Re-raise the RateLimitError
                    raise
            else:
                return response

    def get(self, url, params=None, **kwargs):
        """Sends a GET request.

        Args:
            url(basestring): The URL of the API endpoint.
            params(dict): The parameters for the HTTP GET request.
            **kwargs:
                erc(int): The expected (success) response code for the request.
                others: Passed on to the requests package.

        Raises:
            ApiError: If anything other than the expected response code is
                returned by the Webex Teams API endpoint.

        """
        check_type(url, basestring, may_be_none=False)
        check_type(params, dict)

        # Expected response code
        erc = kwargs.pop('erc', EXPECTED_RESPONSE_CODE['GET'])

        response = self.request('GET', url, erc, params=params, **kwargs)
        return extract_and_parse_json(response)

    def get_pages(self, url, params=None, **kwargs):
        """Return a generator that GETs and yields pages of data.

        Provides native support for RFC5988 Web Linking.

        Args:
            url(basestring): The URL of the API endpoint.
            params(dict): The parameters for the HTTP GET request.
            **kwargs:
                erc(int): The expected (success) response code for the request.
                others: Passed on to the requests package.

        Raises:
            ApiError: If anything other than the expected response code is
                returned by the Webex Teams API endpoint.

        """
        check_type(url, basestring, may_be_none=False)
        check_type(params, dict)

        # Expected response code
        erc = kwargs.pop('erc', EXPECTED_RESPONSE_CODE['GET'])

        # First request
        response = self.request('GET', url, erc, params=params, **kwargs)

        while True:
            yield extract_and_parse_json(response)

            if response.links.get('next'):
                next_url = response.links.get('next').get('url')

                # Patch for Webex Teams 'max=null' in next URL bug.
                # Testing shows that patch is no longer needed; raising a
                # warnning if it is still taking effect;
                # considering for future removal
                next_url = _fix_next_url(next_url)

                # Subsequent requests
                response = self.request('GET', next_url, erc, **kwargs)

            else:
                break

    def get_items(self, url, params=None, **kwargs):
        """Return a generator that GETs and yields individual JSON `items`.

        Yields individual `items` from Webex Teams's top-level {'items': [...]}
        JSON objects. Provides native support for RFC5988 Web Linking.  The
        generator will request additional pages as needed until all items have
        been returned.

        Args:
            url(basestring): The URL of the API endpoint.
            params(dict): The parameters for the HTTP GET request.
            **kwargs:
                erc(int): The expected (success) response code for the request.
                others: Passed on to the requests package.

        Raises:
            ApiError: If anything other than the expected response code is
                returned by the Webex Teams API endpoint.
            MalformedResponse: If the returned response does not contain a
                top-level dictionary with an 'items' key.

        """
        # Get generator for pages of JSON data
        pages = self.get_pages(url, params=params, **kwargs)

        for json_page in pages:
            assert isinstance(json_page, dict)

            items = json_page.get('items')

            if items is None:
                error_message = "'items' key not found in JSON data: " \
                                "{!r}".format(json_page)
                raise MalformedResponse(error_message)

            else:
                for item in items:
                    yield item

    def post(self, url, json=None, data=None, **kwargs):
        """Sends a POST request.

        Args:
            url(basestring): The URL of the API endpoint.
            json: Data to be sent in JSON format in tbe body of the request.
            data: Data to be sent in the body of the request.
            **kwargs:
                erc(int): The expected (success) response code for the request.
                others: Passed on to the requests package.

        Raises:
            ApiError: If anything other than the expected response code is
                returned by the Webex Teams API endpoint.

        """
        check_type(url, basestring, may_be_none=False)

        # Expected response code
        erc = kwargs.pop('erc', EXPECTED_RESPONSE_CODE['POST'])

        response = self.request('POST', url, erc, json=json, data=data,
                                **kwargs)
        return extract_and_parse_json(response)

    def put(self, url, json=None, data=None, **kwargs):
        """Sends a PUT request.

        Args:
            url(basestring): The URL of the API endpoint.
            json: Data to be sent in JSON format in tbe body of the request.
            data: Data to be sent in the body of the request.
            **kwargs:
                erc(int): The expected (success) response code for the request.
                others: Passed on to the requests package.

        Raises:
            ApiError: If anything other than the expected response code is
                returned by the Webex Teams API endpoint.

        """
        check_type(url, basestring, may_be_none=False)

        # Expected response code
        erc = kwargs.pop('erc', EXPECTED_RESPONSE_CODE['PUT'])

        response = self.request('PUT', url, erc, json=json, data=data,
                                **kwargs)
        return extract_and_parse_json(response)

    def delete(self, url, **kwargs):
        """Sends a DELETE request.

        Args:
            url(basestring): The URL of the API endpoint.
            **kwargs:
                erc(int): The expected (success) response code for the request.
                others: Passed on to the requests package.

        Raises:
            ApiError: If anything other than the expected response code is
                returned by the Webex Teams API endpoint.

        """
        check_type(url, basestring, may_be_none=False)

        # Expected response code
        erc = kwargs.pop('erc', EXPECTED_RESPONSE_CODE['DELETE'])

        self.request('DELETE', url, erc, **kwargs)


# -*- coding: utf-8 -*-
"""Webex Teams Guest Issuer API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type,
    dict_from_items_with_values,
    check_response_code
)
from ..response_codes import EXPECTED_RESPONSE_CODE

import jwt
import base64
import requests

API_ENDPOINT = 'jwt'
OBJECT_TYPE = 'guest_issuer_token'

class GuestIssuerAPI(object):
    """Webex Teams Guest Issuer API.

    Wraps the Webex Teams Guest Issuer API and exposes the API as native
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Initialize a new GuestIssuerAPI object with the provided RestSession

        Args:
            session(RestSession): The RESTful session object to be used for
            API calls to the Webex Teams service

        Raises:
            TypeError: If the parameter types are incorrect
        """
        check_type(session, RestSession)

        super(GuestIssuerAPI, self).__init__()

        self._session = session
        self._object_factory = object_factory

    def create(self, subject, displayName, issuerToken, expiration, secret):
        """Create a new guest issuer using the provided issuer token.

        This function returns a guest issuer with an api access token.

        Args:
            subject(basestring): Unique and public identifier
            displayName(basestring): Display Name of the guest user
            issuerToken(basestring): Issuer token from developer hub
            expiration(basestring): Expiration time as a unix timestamp
            secret(basestring): The secret used to sign your guest issuers

        Returns:
            GuestIssuerToken: A Guest Issuer with a valid access token.

        Raises:
            TypeError: If the parameter types are incorrect
            ApiError: If the webex teams cloud returns an error.
        """
        check_type(subject, basestring)
        check_type(displayName, basestring)
        check_type(issuerToken, basestring)
        check_type(expiration, basestring)
        check_type(secret, basestring)

        payload = {
            "sub": subject,
            "name": displayName,
            "iss": issuerToken,
            "exp": expiration
        }

        key = base64.b64decode(secret)
        jwt_token = jwt.encode(payload, key, algorithm='HS256')

        url = self._session.base_url + API_ENDPOINT + "/" + "login"
        headers = {
            'Authorization': "Bearer " + jwt_token.decode('utf-8')
        }
        response = requests.post(url, headers=headers)
        check_response_code(response, EXPECTED_RESPONSE_CODE['GET'])

        return self._object_factory(OBJECT_TYPE, response.json())


# -*- coding: utf-8 -*-
"""Webex Teams Messages API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring
from requests_toolbelt import MultipartEncoder

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type, dict_from_items_with_values, is_local_file, is_web_url,
    open_local_file,
)

API_ENDPOINT = 'messages'
OBJECT_TYPE = 'message'

class MessagesAPI(object):
    """Webex Teams Messages API.

    Wraps the Webex Teams Messages API and exposes the API as native Python
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Init a new MessagesAPI object with the provided RestSession.

        Args:
            session(RestSession): The RESTful session object to be used for
                API calls to the Webex Teams service.

        Raises:
            TypeError: If the parameter types are incorrect.

        """
        check_type(session, RestSession, may_be_none=False)
        super(MessagesAPI, self).__init__()
        self._session = session
        self._object_factory = object_factory

    @generator_container
    def list(self, roomId, mentionedPeople=None, before=None,
             beforeMessage=None, max=None, **request_parameters):
        """Lists messages in a room.

        Each message will include content attachments if present.

        The list API sorts the messages in descending order by creation date.

        This method supports Webex Teams's implementation of RFC5988 Web
        Linking to provide pagination support.  It returns a generator
        container that incrementally yields all messages returned by the
        query.  The generator will automatically request additional 'pages' of
        responses from Webex as needed until all responses have been returned.
        The container makes the generator safe for reuse.  A new API call will
        be made, using the same parameters that were specified when the
        generator was created, every time a new iterator is requested from the
        container.

        Args:
            roomId(basestring): List messages for a room, by ID.
            mentionedPeople(basestring): List messages where the caller is
                mentioned by specifying "me" or the caller `personId`.
            before(basestring): List messages sent before a date and time, in
                ISO8601 format.
            beforeMessage(basestring): List messages sent before a message,
                by ID.
            max(int): Limit the maximum number of items returned from the Webex
                Teams service per request.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            GeneratorContainer: A GeneratorContainer which, when iterated,
            yields the messages returned by the Webex Teams query.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(roomId, basestring, may_be_none=False)
        check_type(mentionedPeople, basestring)
        check_type(before, basestring)
        check_type(beforeMessage, basestring)
        check_type(max, int)

        params = dict_from_items_with_values(
            request_parameters,
            roomId=roomId,
            mentionedPeople=mentionedPeople,
            before=before,
            beforeMessage=beforeMessage,
            max=max,
        )

        # API request - get items
        items = self._session.get_items(API_ENDPOINT, params=params)

        # Yield message objects created from the returned items JSON objects
        for item in items:
            yield self._object_factory(OBJECT_TYPE, item)

    def create(self, roomId=None, toPersonId=None, toPersonEmail=None,
               text=None, markdown=None, files=None, **request_parameters):
        """Post a message, and optionally a attachment, to a room.

        The files parameter is a list, which accepts multiple values to allow
        for future expansion, but currently only one file may be included with
        the message.

        Args:
            roomId(basestring): The room ID.
            toPersonId(basestring): The ID of the recipient when sending a
                private 1:1 message.
            toPersonEmail(basestring): The email address of the recipient when
                sending a private 1:1 message.
            text(basestring): The message, in plain text. If `markdown` is
                specified this parameter may be optionally used to provide
                alternate text for UI clients that do not support rich text.
            markdown(basestring): The message, in markdown format.
            files(`list`): A list of public URL(s) or local path(s) to files to
                be posted into the room. Only one file is allowed per message.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            Message: A Message object with the details of the created message.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.
            ValueError: If the files parameter is a list of length > 1, or if
                the string in the list (the only element in the list) does not
                contain a valid URL or path to a local file.

        """
        check_type(roomId, basestring)
        check_type(toPersonId, basestring)
        check_type(toPersonEmail, basestring)
        check_type(text, basestring)
        check_type(markdown, basestring)
        check_type(files, list)
        if files:
            if len(files) != 1:
                raise ValueError("The length of the `files` list is greater "
                                 "than one (1). The files parameter is a "
                                 "list, which accepts multiple values to "
                                 "allow for future expansion, but currently "
                                 "only one file may be included with the "
                                 "message.")
            check_type(files[0], basestring)

        post_data = dict_from_items_with_values(
            request_parameters,
            roomId=roomId,
            toPersonId=toPersonId,
            toPersonEmail=toPersonEmail,
            text=text,
            markdown=markdown,
            files=files,
        )

        # API request
        if not files or is_web_url(files[0]):
            # Standard JSON post
            json_data = self._session.post(API_ENDPOINT, json=post_data)

        elif is_local_file(files[0]):
            # Multipart MIME post
            try:
                post_data['files'] = open_local_file(files[0])
                multipart_data = MultipartEncoder(post_data)
                headers = {'Content-type': multipart_data.content_type}
                json_data = self._session.post(API_ENDPOINT,
                                               headers=headers,
                                               data=multipart_data)
            finally:
                post_data['files'].file_object.close()

        else:
            raise ValueError("The `files` parameter does not contain a vaild "
                             "URL or path to a local file.")

        # Return a message object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def get(self, messageId):
        """Get the details of a message, by ID.

        Args:
            messageId(basestring): The ID of the message to be retrieved.

        Returns:
            Message: A Message object with the details of the requested
            message.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(messageId, basestring, may_be_none=False)

        # API request
        json_data = self._session.get(API_ENDPOINT + '/' + messageId)

        # Return a message object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def delete(self, messageId):
        """Delete a message.

        Args:
            messageId(basestring): The ID of the message to be deleted.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(messageId, basestring, may_be_none=False)

        # API request
        self._session.delete(API_ENDPOINT + '/' + messageId)


# -*- coding: utf-8 -*-
"""Webex Teams People API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type,
    dict_from_items_with_values,
)

API_ENDPOINT = 'people'
OBJECT_TYPE = 'person'

class PeopleAPI(object):
    """Webex Teams People API.

    Wraps the Webex Teams People API and exposes the API as native Python
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Initialize a new PeopleAPI object with the provided RestSession.

        Args:
            session(RestSession): The RESTful session object to be used for
                API calls to the Webex Teams service.

        Raises:
            TypeError: If the parameter types are incorrect.

        """
        check_type(session, RestSession)

        super(PeopleAPI, self).__init__()

        self._session = session
        self._object_factory = object_factory

    @generator_container
    def list(self, email=None, displayName=None, id=None, orgId=None, max=None,
             **request_parameters):
        """List people

        This method supports Webex Teams's implementation of RFC5988 Web
        Linking to provide pagination support.  It returns a generator
        container that incrementally yields all people returned by the
        query.  The generator will automatically request additional 'pages' of
        responses from Webex as needed until all responses have been returned.
        The container makes the generator safe for reuse.  A new API call will
        be made, using the same parameters that were specified when the
        generator was created, every time a new iterator is requested from the
        container.

        Args:
            email(basestring): The e-mail address of the person to be found.
            displayName(basestring): The complete or beginning portion of
                the displayName to be searched.
            id(basestring): List people by ID. Accepts up to 85 person IDs
                separated by commas.
            orgId(basestring): The organization ID.
            max(int): Limit the maximum number of items returned from the Webex
                Teams service per request.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            GeneratorContainer: A GeneratorContainer which, when iterated,
            yields the people returned by the Webex Teams query.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(id, basestring)
        check_type(email, basestring)
        check_type(displayName, basestring)
        check_type(orgId, basestring)
        check_type(max, int)

        params = dict_from_items_with_values(
            request_parameters,
            id=id,
            email=email,
            displayName=displayName,
            orgId=orgId,
            max=max,
        )

        # API request - get items
        items = self._session.get_items(API_ENDPOINT, params=params)

        # Yield person objects created from the returned items JSON objects
        for item in items:
            yield self._object_factory(OBJECT_TYPE, item)

    def create(self, emails, displayName=None, firstName=None, lastName=None,
               avatar=None, orgId=None, roles=None, licenses=None,
               **request_parameters):
        """Create a new user account for a given organization

        Only an admin can create a new user account.

        Args:
            emails(`list`): Email address(es) of the person (list of strings).
            displayName(basestring): Full name of the person.
            firstName(basestring): First name of the person.
            lastName(basestring): Last name of the person.
            avatar(basestring): URL to the person's avatar in PNG format.
            orgId(basestring): ID of the organization to which this
                person belongs.
            roles(`list`): Roles of the person (list of strings containing
                the role IDs to be assigned to the person).
            licenses(`list`): Licenses allocated to the person (list of
                strings - containing the license IDs to be allocated to the
                person).
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            Person: A Person object with the details of the created person.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(emails, list, may_be_none=False)
        check_type(displayName, basestring)
        check_type(firstName, basestring)
        check_type(lastName, basestring)
        check_type(avatar, basestring)
        check_type(orgId, basestring)
        check_type(roles, list)
        check_type(licenses, list)

        post_data = dict_from_items_with_values(
            request_parameters,
            emails=emails,
            displayName=displayName,
            firstName=firstName,
            lastName=lastName,
            avatar=avatar,
            orgId=orgId,
            roles=roles,
            licenses=licenses,
        )

        # API request
        json_data = self._session.post(API_ENDPOINT, json=post_data)

        # Return a person object created from the returned JSON object
        return self._object_factory(OBJECT_TYPE, json_data)

    def get(self, personId):
        """Get a person's details, by ID.

        Args:
            personId(basestring): The ID of the person to be retrieved.

        Returns:
            Person: A Person object with the details of the requested person.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(personId, basestring, may_be_none=False)

        # API request
        json_data = self._session.get(API_ENDPOINT + '/' + personId)

        # Return a person object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def update(self, personId, emails=None, displayName=None, firstName=None,
               lastName=None, avatar=None, orgId=None, roles=None,
               licenses=None, **request_parameters):
        """Update details for a person, by ID.

        Only an admin can update a person's details.

        Email addresses for a person cannot be changed via the Webex Teams API.

        Include all details for the person. This action expects all user
        details to be present in the request. A common approach is to first GET
        the person's details, make changes, then PUT both the changed and
        unchanged values.

        Args:
            personId(basestring): The person ID.
            emails(`list`): Email address(es) of the person (list of strings).
            displayName(basestring): Full name of the person.
            firstName(basestring): First name of the person.
            lastName(basestring): Last name of the person.
            avatar(basestring): URL to the person's avatar in PNG format.
            orgId(basestring): ID of the organization to which this
                person belongs.
            roles(`list`): Roles of the person (list of strings containing
                the role IDs to be assigned to the person).
            licenses(`list`): Licenses allocated to the person (list of
                strings - containing the license IDs to be allocated to the
                person).
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            Person: A Person object with the updated details.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(emails, list)
        check_type(displayName, basestring)
        check_type(firstName, basestring)
        check_type(lastName, basestring)
        check_type(avatar, basestring)
        check_type(orgId, basestring)
        check_type(roles, list)
        check_type(licenses, list)

        put_data = dict_from_items_with_values(
            request_parameters,
            emails=emails,
            displayName=displayName,
            firstName=firstName,
            lastName=lastName,
            avatar=avatar,
            orgId=orgId,
            roles=roles,
            licenses=licenses,
        )

        # API request
        json_data = self._session.put(API_ENDPOINT + '/' + personId,
                                      json=put_data)

        # Return a person object created from the returned JSON object
        return self._object_factory(OBJECT_TYPE, json_data)

    def delete(self, personId):
        """Remove a person from the system.

        Only an admin can remove a person.

        Args:
            personId(basestring): The ID of the person to be deleted.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(personId, basestring, may_be_none=False)

        # API request
        self._session.delete(API_ENDPOINT + '/' + personId)

    def me(self):
        """Get the details of the person accessing the API.

        Raises:
            ApiError: If the Webex Teams cloud returns an error.

        """
        # API request
        json_data = self._session.get(API_ENDPOINT + '/me')

        # Return a person object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)


# -*- coding: utf-8 -*-
"""Webex Teams Roles API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type,
    dict_from_items_with_values,
)

API_ENDPOINT = 'roles'
OBJECT_TYPE = 'role'

class RolesAPI(object):
    """Webex Teams Roles API.

    Wraps the Webex Teams Roles API and exposes the API as native Python
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Initialize a new RolesAPI object with the provided RestSession.

        Args:
            session(RestSession): The RESTful session object to be used for
                API calls to the Webex Teams service.

        Raises:
            TypeError: If the parameter types are incorrect.

        """
        check_type(session, RestSession, may_be_none=False)

        super(RolesAPI, self).__init__()

        self._session = session
        self._object_factory = object_factory

    @generator_container
    def list(self, **request_parameters):
        """List all roles.

        Args:
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            GeneratorContainer: A GeneratorContainer which, when iterated,
            yields the roles returned by the Webex Teams query.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        # API request - get items
        items = self._session.get_items(
            API_ENDPOINT,
            params=request_parameters
        )

        # Yield role objects created from the returned JSON objects
        for item in items:
            yield self._object_factory(OBJECT_TYPE, item)

    def get(self, roleId):
        """Get the details of a Role, by ID.

        Args:
            roleId(basestring): The ID of the Role to be retrieved.

        Returns:
            Role: A Role object with the details of the requested Role.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(roleId, basestring, may_be_none=False)

        # API request
        json_data = self._session.get(API_ENDPOINT + '/' + roleId)

        # Return a role object created from the returned JSON object
        return self._object_factory(OBJECT_TYPE, json_data)


# -*- coding: utf-8 -*-
"""Webex Teams Teams-API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type,
    dict_from_items_with_values,
)

API_ENDPOINT = 'teams'
OBJECT_TYPE = 'team'

class TeamsAPI(object):
    """Webex Teams Teams API.

    Wraps the Webex Teams Teams API and exposes the API as native Python
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Initialize a new TeamsAPI object with the provided RestSession.

        Args:
            session(RestSession): The RESTful session object to be used for
                API calls to the Webex Teams service.

        Raises:
            TypeError: If the parameter types are incorrect.

        """
        check_type(session, RestSession, may_be_none=False)

        super(TeamsAPI, self).__init__()

        self._session = session
        self._object_factory = object_factory

    @generator_container
    def list(self, max=None, **request_parameters):
        """List teams to which the authenticated user belongs.

        This method supports Webex Teams's implementation of RFC5988 Web
        Linking to provide pagination support.  It returns a generator
        container that incrementally yields all teams returned by the
        query.  The generator will automatically request additional 'pages' of
        responses from Webex as needed until all responses have been returned.
        The container makes the generator safe for reuse.  A new API call will
        be made, using the same parameters that were specified when the
        generator was created, every time a new iterator is requested from the
        container.

        Args:
            max(int): Limit the maximum number of items returned from the Webex
                Teams service per request.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            GeneratorContainer: A GeneratorContainer which, when iterated,
            yields the teams returned by the Webex Teams query.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(max, int)

        params = dict_from_items_with_values(
            request_parameters,
            max=max,
        )

        # API request - get items
        items = self._session.get_items(API_ENDPOINT, params=params)

        # Yield team objects created from the returned items JSON objects
        for item in items:
            yield self._object_factory(OBJECT_TYPE, item)

    def create(self, name, **request_parameters):
        """Create a team.

        The authenticated user is automatically added as a member of the team.

        Args:
            name(basestring): A user-friendly name for the team.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            Team: A Team object with the details of the created team.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(name, basestring, may_be_none=False)

        post_data = dict_from_items_with_values(
            request_parameters,
            name=name,
        )

        # API request
        json_data = self._session.post(API_ENDPOINT, json=post_data)

        # Return a team object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def get(self, teamId):
        """Get the details of a team, by ID.

        Args:
            teamId(basestring): The ID of the team to be retrieved.

        Returns:
            Team: A Team object with the details of the requested team.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(teamId, basestring, may_be_none=False)

        # API request
        json_data = self._session.get(API_ENDPOINT + '/' + teamId)

        # Return a team object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def update(self, teamId, name=None, **request_parameters):
        """Update details for a team, by ID.

        Args:
            teamId(basestring): The team ID.
            name(basestring): A user-friendly name for the team.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            Team: A Team object with the updated Webex Teams team details.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(teamId, basestring, may_be_none=False)
        check_type(name, basestring)

        put_data = dict_from_items_with_values(
            request_parameters,
            name=name,
        )

        # API request
        json_data = self._session.put(API_ENDPOINT + '/' + teamId,
                                      json=put_data)

        # Return a team object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)

    def delete(self, teamId):
        """Delete a team.

        Args:
            teamId(basestring): The ID of the team to be deleted.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(teamId, basestring, may_be_none=False)

        # API request
        self._session.delete(API_ENDPOINT + '/' + teamId)


# -*- coding: utf-8 -*-
"""Webex Teams Events API wrapper.

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *

from past.builtins import basestring

from ..generator_containers import generator_container
from ..restsession import RestSession
from ..utils import (
    check_type,
    dict_from_items_with_values,
)

API_ENDPOINT = 'events'
OBJECT_TYPE = 'event'

class EventsAPI(object):
    """Webex Teams Events API.

    Wraps the Webex Teams Events API and exposes the API as native Python
    methods that return native Python objects.

    """

    def __init__(self, session, object_factory):
        """Initialize a new EventsAPI object with the provided RestSession.

        Args:
            session(RestSession): The RESTful session object to be used for
                API calls to the Webex Teams service.

        Raises:
            TypeError: If the parameter types are incorrect.

        """
        check_type(session, RestSession, may_be_none=False)

        super(EventsAPI, self).__init__()

        self._session = session
        self._object_factory = object_factory

    @generator_container
    def list(self, resource=None, type=None, actorId=None, _from=None, to=None,
             max=None, **request_parameters):
        """List events.

        List events in your organization. Several query parameters are
        available to filter the response.

        Note: `from` is a keyword in Python and may not be used as a variable
        name, so we had to use `_from` instead.

        This method supports Webex Teams's implementation of RFC5988 Web
        Linking to provide pagination support.  It returns a generator
        container that incrementally yields all events returned by the
        query.  The generator will automatically request additional 'pages' of
        responses from Wevex as needed until all responses have been returned.
        The container makes the generator safe for reuse.  A new API call will
        be made, using the same parameters that were specified when the
        generator was created, every time a new iterator is requested from the
        container.

        Args:
            resource(basestring): Limit results to a specific resource type.
                Possible values: "messages", "memberships".
            type(basestring): Limit results to a specific event type. Possible
                values: "created", "updated", "deleted".
            actorId(basestring): Limit results to events performed by this
                person, by ID.
            _from(basestring): Limit results to events which occurred after a
                date and time, in ISO8601 format (yyyy-MM-dd'T'HH:mm:ss.SSSZ).
            to(basestring): Limit results to events which occurred before a
                date and time, in ISO8601 format (yyyy-MM-dd'T'HH:mm:ss.SSSZ).
            max(int): Limit the maximum number of items returned from the Webex
                Teams service per request.
            **request_parameters: Additional request parameters (provides
                support for parameters that may be added in the future).

        Returns:
            GeneratorContainer: A GeneratorContainer which, when iterated,
            yields the events returned by the Webex Teams query.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(resource, basestring)
        check_type(type, basestring)
        check_type(actorId, basestring)
        check_type(_from, basestring)
        check_type(to, basestring)
        check_type(max, int)

        params = dict_from_items_with_values(
            request_parameters,
            resource=resource,
            type=type,
            actorId=actorId,
            _from=_from,
            to=to,
            max=max,
        )

        if _from:
            params["from"] = params.pop("_from")

        # API request - get items
        items = self._session.get_items(API_ENDPOINT, params=params)

        # Yield event objects created from the returned items JSON objects
        for item in items:
            yield self._object_factory(OBJECT_TYPE, item)

    def get(self, eventId):
        """Get the details for an event, by event ID.

        Args:
            eventId(basestring): The ID of the event to be retrieved.

        Returns:
            Event: A event object with the details of the requested room.

        Raises:
            TypeError: If the parameter types are incorrect.
            ApiError: If the Webex Teams cloud returns an error.

        """
        check_type(eventId, basestring, may_be_none=False)

        # API request
        json_data = self._session.get(API_ENDPOINT + '/' + eventId)

        # Return a room object created from the response JSON data
        return self._object_factory(OBJECT_TYPE, json_data)


# -*- coding: utf-8 -*-
"""Model Webex Teams JSON objects as native Python objects.

Classes:
    ImmutableData: Models Webex Teams JSON objects as native Python objects.

The ImmutableData class models any JSON object passed to it as a string or
Python dictionary as a native Python object; providing attribute access using
native dot-syntax (`object.attribute`).

Copyright (c) 2016-2019 Cisco and/or its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)

from builtins import *
import json
from collections import defaultdict

from webexteamssdk.utils import json_dict
from .mixins.access_token import AccessTokenBasicPropertiesMixin
from .mixins.event import EventBasicPropertiesMixin
from .mixins.license import LicenseBasicPropertiesMixin
from .mixins.membership import MembershipBasicPropertiesMixin
from .mixins.message import MessageBasicPropertiesMixin
from .mixins.organization import OrganizationBasicPropertiesMixin
from .mixins.person import PersonBasicPropertiesMixin
from .mixins.role import RoleBasicPropertiesMixin
from .mixins.room import RoomBasicPropertiesMixin
from .mixins.team import TeamBasicPropertiesMixin
from .mixins.team_membership import TeamMembershipBasicPropertiesMixin
from .mixins.webhook import WebhookBasicPropertiesMixin
from .mixins.webhook_event import WebhookEventBasicPropertiesMixin
from .mixins.guest_issuer_token import GuestIssuerTokenBasicPropertiesMixin

class ImmutableData(object):
    """Model a Webex Teams JSON object as an immutable native Python object."""

    def __init__(self, json_data):
        """Init a new ImmutableData object from a dictionary or JSON string.

        Args:
            json_data(dict, basestring): Input JSON string or dictionary.

        Raises:
            TypeError: If the input object is not a dictionary or string.

        """
        super(ImmutableData, self).__init__()
        self._json_data = json_dict(json_data)

    def __getattr__(self, item):
        """Provide native attribute access to the JSON object attributes.

        This method is called when attempting to access a object attribute that
        hasn't been defined for the object.  For example trying to access
        object.attribute1 when attribute1 hasn't been defined.

        ImmutableData.__getattr__() checks the original JSON object to see if
        the attribute exists, and if it does, it returns the attribute's value
        from the original JSON object.  This provides native access to all of
        the JSON object's attributes.

        Args:
            item(str): Name of the Attribute being accessed.

        Raises:
            AttributeError:  If the JSON object does not contain the attribute
                requested.

        """
        if item in list(self._json_data.keys()):
            item_data = self._json_data[item]
            if isinstance(item_data, dict):
                return ImmutableData(item_data)
            else:
                return item_data
        else:
            raise AttributeError(
                "'{}' object has no attribute '{}'"
                "".format(self.__class__.__name__, item)
            )

    def __str__(self):
        """A human-readable string representation of this object."""
        class_str = self.__class__.__name__
        json_str = json.dumps(self._json_data, indent=2)
        return "Webex Teams {}:\n{}".format(class_str, json_str)

    def __repr__(self):
        """A string representing this object as valid Python expression."""
        class_str = self.__class__.__name__
        json_str = json.dumps(self._json_data, ensure_ascii=False)
        return "{}({})".format(class_str, repr(json_str))

    @classmethod
    def _serialize(cls, data):
        """Serialize data to an frozen tuple."""
        if hasattr(data, "__hash__") and callable(data.__hash__):
            # If the data is already hashable (should be immutable) return it
            return data
        elif isinstance(data, list):
            # Freeze the elements of the list and return as a tuple
            return tuple((cls._serialize(item) for item in data))
        elif isinstance(data, dict):
            # Freeze the elements of the dictionary, sort them, and return
            # them as a list of tuples
            key_value_tuples = [
                (key, cls._serialize(value))
                for key, value in data.items()
            ]
            key_value_tuples.sort()
            return tuple(key_value_tuples)
        else:
            raise TypeError(
                "Unable to freeze {} data type.".format(type(data))
            )

    def _freeze(self):
        """Freeze this object's JSON data."""
        return self._serialize(self._json_data)

    def __eq__(self, other):
        """Determine if two objects are equal."""
        return isinstance(other, self.__class__) \
            and self._freeze() == other._freeze()

    def __hash__(self):
        """Hash the data object."""
        return hash(self._freeze())

    @property
    def json_data(self):
        """A copy of the data object's JSON data (OrderedDict)."""
        # TODO: When we move to Python v3+ only; use MappingProxyType.
        return self._json_data.copy()

    def to_dict(self):
        """Convert the Webex Teams object data to a dictionary."""
        return dict(self._json_data)

    def to_json(self, **kwargs):
        """Convert the Webex Teams object data to JSON.

        Any keyword arguments provided are passed through the Python JSON
        encoder.

        """
        return json.dumps(self._json_data, **kwargs)

class AccessToken(ImmutableData, AccessTokenBasicPropertiesMixin):
    """Webex Teams Access-Token data model."""

class Event(ImmutableData, EventBasicPropertiesMixin):
    """Webex Teams Event data model."""

class License(ImmutableData, LicenseBasicPropertiesMixin):
    """Webex Teams License data model."""

class Membership(ImmutableData, MembershipBasicPropertiesMixin):
    """Webex Teams Membership data model."""

class Message(ImmutableData, MessageBasicPropertiesMixin):
    """Webex Teams Message data model."""

class Organization(ImmutableData, OrganizationBasicPropertiesMixin):
    """Webex Teams Organization data model."""

class Person(ImmutableData, PersonBasicPropertiesMixin):
    """Webex Teams Person data model."""

class Role(ImmutableData, RoleBasicPropertiesMixin):
    """Webex Teams Role data model."""

class Room(ImmutableData, RoomBasicPropertiesMixin):
    """Webex Teams Room data model."""

class Team(ImmutableData, TeamBasicPropertiesMixin):
    """Webex Teams Team data model."""

class TeamMembership(ImmutableData, TeamMembershipBasicPropertiesMixin):
    """Webex Teams Team-Membership data model."""

class Webhook(ImmutableData, WebhookBasicPropertiesMixin):
    """Webex Teams Webhook data model."""

class WebhookEvent(ImmutableData, WebhookEventBasicPropertiesMixin):
    """Webex Teams Webhook-Events data model."""

    @property
    def data(self):
        """The event resource data."""
        return ImmutableData(self._json_data.get('data'))

class GuestIssuerToken(ImmutableData, GuestIssuerTokenBasicPropertiesMixin):
    """Webex Teams Guest Issuer Token data model"""

immutable_data_models = defaultdict(
    lambda: ImmutableData,
    access_token=AccessToken,
    event=Event,
    license=License,
    membership=Membership,
    message=Message,
    organization=Organization,
    person=Person,
    role=Role,
    room=Room,
    team=Team,
    team_membership=TeamMembership,
    webhook=Webhook,
    webhook_event=WebhookEvent,
    guest_issuer_token=GuestIssuerToken,
)

def immutable_data_factory(model, json_data):
    """Factory function for creating ImmutableData objects.

    Args:
        model(basestring): The data model to use when creating the
            ImmutableData object (message, room, membership, etc.).
        json_data(basestring, dict): The JSON string or dictionary data with
            which to initialize the object.

    Returns:
        ImmutableData: The created ImmutableData object.

    Raises:
        TypeError: If the json_data parameter is not a JSON string or
            dictionary.

    """
    return immutable_data_models[model](json_data)