def update(self, webhookId, **update_attributes): """Update details for a webhook. Args: webhookId(string_types): The webhookId of the webhook to be updated. name(string_types): A user-friendly name for this webhook. targetUrl(string_types): The URL that receives POST requests for each event. Returns: Webhook: With the updated Spark webhook details. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If an update attribute is not provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(webhookId, string_types) # Process update_attributes keyword arguments if not update_attributes: error_message = "At least one **update_attributes keyword " \ "argument must be specified." raise ciscosparkapiException(error_message) # API request json_obj = self._session.put('webhooks/' + webhookId, json=update_attributes) # Return a Webhook object created from the response JSON data return Webhook(json_obj)
def _fix_next_url(next_url): """Remove max=null parameter from URL. Patch for Cisco Spark 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. ciscosparkapiException: 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: error_message = "'next_url' must be a valid API endpoint URL, " \ "minimally containing a scheme, netloc and path." raise ciscosparkapiException(error_message) if parsed_url.query: query_list = parsed_url.query.split('&') if 'max=null' in query_list: query_list.remove('max=null') new_query = '&'.join(query_list) parsed_url = list(parsed_url) parsed_url[4] = new_query return urllib.parse.urlunparse(parsed_url)
def update(self, teamId, **update_attributes): """Update details for a team. Args: teamId(string_types): The teamId of the team to be updated. name(string_types): A user-friendly name for the team. Returns: Team: With the updated Spark team details. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If an update attribute is not provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(teamId, string_types) # Process update_attributes keyword arguments if not update_attributes: error_message = "At least one **update_attributes keyword " \ "argument must be specified." raise ciscosparkapiException(error_message) # API request json_obj = self._session.post('teams/' + teamId, json=update_attributes) # Return a Team object created from the response JSON data return Team(json_obj)
def update(self, membershipId, **update_attributes): """Update details for a membership. Args: membershipId(string_types): The membershipId of the membership to be updated. isModerator(bool): If True, sets the person as a moderator for the room. If False, removes the person as a moderator for the room. Returns: Membership: With the updated Spark membership details. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If an update attribute is not provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(membershipId, string_types) # Process update_attributes keyword arguments if not update_attributes: error_message = "At least one **update_attributes keyword " \ "argument must be specified." raise ciscosparkapiException(error_message) # API request json_obj = self._session.post('memberships/' + membershipId, json=update_attributes) # Return a Membership object created from the response JSON data return Membership(json_obj)
def update(self, membershipId, **update_attributes): """Update details for a team membership. Args: membershipId(basestring): The membershipId of the team membership to be updated. isModerator(bool): If True, sets the person as a moderator for the team. If False, removes the person as a moderator for the team. Returns: TeamMembership: With the updated Spark team membership details. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If an update attribute is not provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(membershipId, basestring) # Process update_attributes keyword arguments if not update_attributes: error_message = "At least one **update_attributes keyword " \ "argument must be specified." raise ciscosparkapiException(error_message) # API request json_obj = self._session.put('team/memberships/' + membershipId, json=update_attributes) # Return a TeamMembership object created from the response JSON data return TeamMembership(json_obj)
def update(self, webhookId, **update_attributes): """Update details for a webhook. Args: webhookId(string_types): The webhookId of the webhook to be updated. **update_attributes: name(string_types): A user-friendly name for this webhook. targetUrl(string_types): The URL that receives POST requests for each event. Returns: Webhook: With the updated Spark webhook details. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If an update attribute is not provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(webhookId, string_types) # Process update_attributes keyword arguments if not update_attributes: error_message = "At least one **update_attributes keyword " \ "argument must be specified." raise ciscosparkapiException(error_message) # API request json_obj = self.session.put('webhooks/' + webhookId, json=update_attributes) # Return a Webhook object created from the response JSON data return Webhook(json_obj)
def validate_base_url(base_url): """Verify that base_url specifies a protocol and network location.""" parsed_url = urllib.parse.urlparse(base_url) if parsed_url.scheme and parsed_url.netloc: return parsed_url.geturl() else: error_message = "base_url must contain a valid scheme (protocol " \ "specifier) and network location (hostname)" raise ciscosparkapiException(error_message)
def list(self, email=None, displayName=None, max=None): """List people by email or displayName. An email address or displayName must be provided. This method supports Cisco Spark's implementation of RFC5988 Web Linking to provide pagination support. It returns a generator container that incrementally yield all people returned by the query. The generator will automatically request additional 'pages' of responses from Spark 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(string_types): The e-mail address of the person to be found. displayName(string_types): The complete or beginning portion of the displayName to be searched. max(int): Limits the maximum number of people returned from the Spark service per request. Returns: GeneratorContainer: When iterated, the GeneratorContainer, yields the people returned by the Cisco Spark query. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If neither an email or displayName argument is specified. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert email is None or isinstance(email, string_types) assert displayName is None or isinstance(displayName, string_types) assert max is None or isinstance(max, int) params = {} if email: params['email'] = email elif displayName: params['displayName'] = displayName else: error_message = "An email or displayName argument must be " \ "specified." raise ciscosparkapiException(error_message) if max: params['max'] = max # API request - get items items = self._session.get_items('people', params=params) # Yield Person objects created from the returned items JSON objects for item in items: yield Person(item)
def create(self, teamId, personId=None, personEmail=None, isModerator=False): """Add someone to a team by Person ID or email address. Add someone to a team by Person ID or email address; optionally making them a moderator. Args: teamId(basestring): ID of the team to which the person will be added. personId(basestring): ID of the person to be added to the team. personEmail(basestring): Email address of the person to be added to the team. isModerator(bool): If True, adds the person as a moderator for the team. If False, adds the person as normal member of the team. Returns: TeamMembership: With the details of the created team membership. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If neither a personId or personEmail are provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(teamId, basestring) assert personId is None or isinstance(personId, basestring) assert personEmail is None or isinstance(personEmail, basestring) assert isModerator is None or isinstance(isModerator, bool) post_data = {} post_data['teamId'] = teamId if personId: post_data['personId'] = personId elif personEmail: post_data['personEmail'] = personEmail else: error_message = "personId or personEmail must be provided to " \ "add a person to a team. Neither were provided." raise ciscosparkapiException(error_message) post_data['isModerator'] = isModerator # API request json_obj = self._session.post('team/memberships', json=post_data) # Return a TeamMembership object created from the response JSON data return TeamMembership(json_obj)
def update(self, personId, **person_attributes): """Update details for a person, by ID. Only an admin can update a person details. Args: personId(string_types): The ID of the person to be updated. **person_attributes emails(list): Email address(es) of the person. (list of strings) CURRENT LIMITATION: Spark (today) only allows you to provide a single email address for a person. The list data type was selected to enable future support for providing multiple email address. displayName(string_types): Full name of the person firstName(string_types): First name of the person lastName(string_types): Last name of the person avatar(string_types): URL to the person's avatar in PNG format orgId(string_types): 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) Returns: Person: With the updated person details. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If an update attribute is not provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(personId, string_types) # Process update_attributes keyword arguments if not person_attributes: error_message = "At least one **update_attributes keyword " \ "argument must be specified." raise ciscosparkapiException(error_message) # API request json_obj = self._session.put('people/' + personId, json=person_attributes) # Return a Person object created from the returned JSON object return Person(json_obj)
def get_items(self, url, params=None, **kwargs): # Get iterator for pages of JSON data pages = self.get_pages(url, params=params, **kwargs) # Process pages for json_page in pages: # Process each page of JSON data yielding the individual JSON # objects contained within the top level 'items' array assert isinstance(json_page, dict) items = json_page.get(u'items') if items is None: error_message = "'items' object not found in JSON data: " \ "{!r}".format(json_page) raise ciscosparkapiException(error_message) else: for item in items: yield item
def create(self, roomId, personId=None, personEmail=None, isModerator=False): """Add someone to a room by Person ID or email address. Add someone to a room by Person ID or email address; optionally making them a moderator. Args: roomId(string_types): ID of the room to which the person will be added. personId(string_types): ID of the person to be added to the room. personEmail(string_types): Email address of the person to be added to the room. isModerator(bool): If True, adds the person as a moderator for the room. If False, adds the person as normal member of the room. Returns: Membership: With the details of the created membership. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If neither a personId or personEmail are provided. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert isinstance(roomId, string_types) assert personId is None or isinstance(personId, string_types) assert personEmail is None or isinstance(personEmail, string_types) assert isModerator is None or isinstance(isModerator, bool) post_data = {} post_data['roomId'] = roomId if personId: post_data['personId'] = personId elif personEmail: post_data['personEmail'] = personEmail else: error_message = "personId or personEmail must be provided to " \ "add a person to a room. Neither were provided." raise ciscosparkapiException(error_message) post_data['isModerator'] = isModerator # API request json_obj = self._session.post('memberships', json=post_data) # Return a Membership object created from the response JSON data return Membership(json_obj)
def list(self, roomId=None, personId=None, personEmail=None, max=None): """List room memberships. By default, lists memberships for rooms to which the authenticated user belongs. Use query parameters to filter the response. Use roomId to list memberships for a room, by ID. Use either personId or personEmail to filter the results. This method supports Cisco Spark's implementation of RFC5988 Web Linking to provide pagination support. It returns a generator container that incrementally yield all memberships returned by the query. The generator will automatically request additional 'pages' of responses from Spark 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(string_types): List memberships for the room with roomId. personId(string_types): Filter results to include only those with personId. personEmail(string_types): Filter results to include only those with personEmail. max(int): Limits the maximum number of memberships returned from the Spark service per request. Returns: GeneratorContainer: When iterated, the GeneratorContainer, yields the memberships returned from the Cisco Spark query. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If a personId or personEmail argument is specified without providing a roomId argument. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert roomId is None or isinstance(roomId, string_types) assert personId is None or isinstance(personId, string_types) assert personEmail is None or isinstance(personEmail, string_types) assert max is None or isinstance(max, int) params = {} if roomId: params['roomId'] = roomId if personId: params['personId'] = personId elif personEmail: params['personEmail'] = personEmail elif personId or personEmail: error_message = "A roomId must be specified. A personId or " \ "personEmail filter may only be specified when " \ "requesting the memberships for a room with the " \ "roomId argument." raise ciscosparkapiException(error_message) if max: params['max'] = max # API request - get items items = self._session.get_items('memberships', params=params) # Yield Person objects created from the returned items JSON objects for item in items: yield Membership(item)
def __init__(self, access_token=None, base_url=DEFAULT_BASE_URL, timeout=DEFAULT_TIMEOUT): """Create a new CiscoSparkAPI object. An access token must be used when interacting with the Cisco Spark API. This package supports two methods for you to provide that access token: 1. You may manually specify the access token via the access_token argument, when creating a new CiscoSparkAPI object. 2. If an access_token argument is not supplied, the package checks for a SPARK_ACCESS_TOKEN environment variable. A ciscosparkapiException is raised if an access token is not provided via one of these two methods. Args: access_token(basestring): The access token to be used for API calls to the Cisco Spark service. Defaults to checking for a SPARK_ACCESS_TOKEN environment variable. base_url(basestring): The base URL to be prefixed to the individual API endpoint suffixes. Defaults to ciscosparkapi.DEFAULT_BASE_URL. timeout(int): Timeout (in seconds) for RESTful HTTP requests. Defaults to ciscosparkapi.DEFAULT_TIMEOUT. Returns: CiscoSparkAPI: A new CiscoSparkAPI object. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If an access token is not provided via the access_token argument or SPARK_ACCESS_TOKEN environment variable. """ # Process args assert access_token is None or isinstance(access_token, basestring) assert isinstance(base_url, basestring) assert isinstance(timeout, int) spark_access_token = os.environ.get(ACCESS_TOKEN_ENVIRONMENT_VARIABLE) access_token = access_token if access_token else spark_access_token if not access_token: error_message = "You must provide an Spark access token to " \ "interact with the Cisco Spark APIs, either via " \ "a SPARK_ACCESS_TOKEN environment variable " \ "or via the access_token argument." raise ciscosparkapiException(error_message) session_args = {u'timeout': timeout} # Create the API session # All of the API calls associated with a CiscoSparkAPI object will # leverage a single RESTful 'session' connecting to the Cisco Spark # cloud. self._session = RestSession(access_token, base_url, **session_args) # Spark API wrappers self.people = PeopleAPI(self._session) self.rooms = RoomsAPI(self._session) self.memberships = MembershipsAPI(self._session) self.messages = MessagesAPI(self._session) self.teams = TeamsAPI(self._session) self.team_memberships = TeamMembershipsAPI(self._session) self.webhooks = WebhooksAPI(self._session) self.organizations = OrganizationsAPI(self._session) self.licenses = LicensesAPI(self._session) self.roles = RolesAPI(self._session) self.access_tokens = AccessTokensAPI(self.base_url, timeout=timeout)
def create(self, roomId=None, toPersonId=None, toPersonEmail=None, text=None, markdown=None, files=None): """Posts a message to a room. Posts a message, and optionally, a media content attachment, to a room. You must specify either a roomId, toPersonId or toPersonEmail when posting a message, and you must supply some message content (text, markdown, files). Args: roomId(string_types): The room ID. toPersonId(string_types): The ID of the recipient when sending a private 1:1 message. toPersonEmail(string_types): The email address of the recipient when sending a private 1:1 message. text(string_types): The message, in plain text. If markdown is specified this parameter may be optionally used to provide alternate text forUI clients that do not support rich text. markdown(string_types): The message, in markdown format. files(list): A list containing local paths or URL references for the message attachment(s). The files attribute currently only takes a list containing one (1) filename or URL as an input. This is a Spark API limitation that may be lifted at a later date. Returns: Message: With the details of the created message. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If the required arguments are not specified. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert roomId is None or isinstance(roomId, string_types) assert toPersonId is None or isinstance(toPersonId, string_types) assert toPersonEmail is None or isinstance(toPersonEmail, string_types) assert text is None or isinstance(text, string_types) assert markdown is None or isinstance(markdown, string_types) assert files is None or isinstance(files, list) post_data = {} # Where is message to be posted? if roomId: post_data['roomId'] = roomId elif toPersonId: post_data['toPersonId'] = toPersonId elif toPersonEmail: post_data['toPersonEmail'] = toPersonEmail else: error_message = "You must specify a roomId, toPersonId, or " \ "toPersonEmail to which you want to post a new " \ "message." raise ciscosparkapiException(error_message) # Ensure some message 'content' is provided. if not text and not markdown and not files: error_message = "You must supply some message content (text, " \ "markdown, files) when posting a message." raise ciscosparkapiException(error_message) # Process the content. if text: post_data['text'] = text if markdown: post_data['markdown'] = markdown upload_local_file = False if files: if len(files) > 1: error_message = "The files attribute currently only takes a " \ "list containing one (1) filename or URL as " \ "an input. This is a Spark API limitation " \ "that may be lifted at a later date." raise ciscosparkapiException(error_message) if is_web_url(files[0]): post_data['files'] = files elif is_local_file(files[0]): upload_local_file = True post_data['files'] = open_local_file(files[0]) else: error_message = "The provided files argument does not " \ "contain a valid URL or local file path." raise ciscosparkapiException(error_message) # API request if upload_local_file: try: multipart_data = MultipartEncoder(post_data) headers = {'Content-type': multipart_data.content_type} json_obj = self._session.post('messages', data=multipart_data, headers=headers) finally: post_data['files'].file_object.close() else: json_obj = self._session.post('messages', json=post_data) # Return a Message object created from the response JSON data return Message(json_obj)
def list(self, roomId=None, personId=None, personEmail=None, max=None): """List room memberships. By default, lists memberships for rooms to which the authenticated user belongs. Use query parameters to filter the response. Use roomId to list memberships for a room, by ID. Use either personId or personEmail to filter the results. This method supports Cisco Spark's implementation of RFC5988 Web Linking to provide pagination support. It returns a generator container that incrementally yield all memberships returned by the query. The generator will automatically request additional 'pages' of responses from Spark 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(string_types): List memberships for the room with roomId. personId(string_types): Filter results to include only those with personId. personEmail(string_types): Filter results to include only those with personEmail. max(int): Limits the maximum number of memberships returned from the Spark service per request. Yields: Membership: The the next membership from the Cisco Spark query. Raises: AssertionError: If the parameter types are incorrect. ciscosparkapiException: If a personId or personEmail argument is specified without providing a roomId argument. SparkApiError: If the Cisco Spark cloud returns an error. """ # Process args assert roomId is None or isinstance(roomId, string_types) assert personId is None or isinstance(personId, string_types) assert personEmail is None or isinstance(personEmail, string_types) assert max is None or isinstance(max, int) params = {} if roomId: params['roomId'] = roomId if personId: params['personId'] = personId elif personEmail: params['personEmail'] = personEmail elif personId or personEmail: error_message = "A roomId must be specified. A personId or " \ "personEmail filter may only be specified when " \ "requesting the memberships for a room with the " \ "roomId argument." raise ciscosparkapiException(error_message) if max: params['max'] = max # API request - get items items = self.session.get_items('memberships', params=params) # Yield Person objects created from the returned items JSON objects for item in items: yield Membership(item)