def get(path): if url is not None or username is not None or password is not None: try: ctx_auth = AuthenticationContext(url) if ctx_auth.acquire_token_for_user(username, password): ClientRequest(ctx_auth) request_url = "{0}{1}".format(url, path) log.info("Getting '{0}' from '{1}'".format(path, url)) options = RequestOptions("{0}".format(request_url)) options.set_header('Accept', 'application/json') options.set_header('Content-Type', 'application/text') ctx_auth.authenticate_request(options) data = requests.get(url=options.url, headers=options.headers, auth=options.auth) return Response(data.content, mimetype="application/text") except BaseException as e: log.exception("Failed to get resource '{0}': {1}".format( request_url, e)) raise e else: raise ValueError( "Missing one or more required environment variables (baseurl, username, password)" )
class ClientContext(ClientRuntimeContext): """SharePoint client context""" def __init__(self, base_url, auth_context=None): """ Instantiates a SharePoint client context :param str base_url: Absolute Web or Site Url :param AuthenticationContext or None auth_context: Authentication context """ if base_url.endswith("/"): base_url = base_url[:len(base_url) - 1] if auth_context is None: self._auth_context = AuthenticationContext(authority_url=base_url) else: self._auth_context = auth_context super(ClientContext, self).__init__() self._web = None self._site = None self._base_url = base_url self._ctx_web_info = None self._pending_request = None def create_safe_url(self, orig_url, relative=True): """ Creates a safe url :type orig_url: str :type relative: bool """ if is_absolute_url(orig_url) and not relative: return orig_url site_path = urlparse(self.base_url).path root_site_url = self.base_url.replace(site_path, "") url = orig_url if orig_url.startswith(site_path) else "/".join( [site_path, orig_url]) return url if relative else "".join([root_site_url, url]) @staticmethod def from_url(abs_url): """ Constructs ClientContext from absolute Url :param str abs_url: Absolute Url to resource :return: ClientContext """ base_url = resolve_base_url(abs_url) ctx = ClientContext(base_url) result = Web.get_web_url_from_page_url(ctx, abs_url) def _init_context_for_web(resp): """ :type resp: requests.Response """ resp.raise_for_status() ctx._base_url = result.value ctx.after_execute(_init_context_for_web) return ctx def with_client_certificate(self, tenant, client_id, thumbprint, cert_path, **kwargs): """Creates authenticated SharePoint context via certificate credentials :param str tenant: Tenant name :param str cert_path: Path to A PEM encoded certificate private key. :param str thumbprint: Hex encoded thumbprint of the certificate. :param str client_id: The OAuth client id of the calling application. :param list[str] scopes (optional): Scopes requested to access a protected API (a resource) """ self.authentication_context.with_client_certificate( tenant, client_id, thumbprint, cert_path, **kwargs) return self def with_access_token(self, token_func): """ :type token_func: () -> TokenResponse """ self.authentication_context.register_provider(token_func) return self def with_user_credentials(self, username, password, allow_ntlm=False, browser_mode=False): """ Assigns credentials :type username: str :type password: str :type allow_ntlm: bool :type browser_mode: bool """ self.authentication_context.register_provider( UserCredential(username, password), allow_ntlm=allow_ntlm, browser_mode=browser_mode) return self def with_credentials(self, credentials): """ Assigns credentials :type credentials: UserCredential or ClientCredential """ self.authentication_context.register_provider(credentials) return self def execute_batch(self, items_per_batch=100): """ Construct and submit a batch request :param int items_per_batch: Maximum to be selected for bulk operation """ batch_request = ODataBatchRequest(self) def _prepare_batch_request(request): self.ensure_form_digest(request) batch_request.beforeExecute += _prepare_batch_request all_queries = [qry for qry in self.pending_request()] for i in range_or_xrange(0, len(all_queries), items_per_batch): queries = all_queries[i:i + items_per_batch] batch_request.add_query(BatchQuery(self, queries)) batch_request.execute_query() return self def build_request(self, query): """ :type query: office365.runtime.queries.client_query.ClientQuery """ request = super(ClientContext, self).build_request(query) self._build_modification_query(request) return request def pending_request(self): """ Provides access to underlying request instance :return: ODataRequest """ if self._pending_request is None: self._pending_request = ODataRequest(self, JsonLightFormat()) self._pending_request.beforeExecute += self._build_modification_query return self._pending_request def ensure_form_digest(self, request_options): """ :type request_options: RequestOptions """ if self._ctx_web_info is None or not self._ctx_web_info.is_valid: self._ctx_web_info = self.get_context_web_information( request_options=request_options) request_options.set_header('X-RequestDigest', self._ctx_web_info.FormDigestValue) def get_context_web_information(self, request_options=None): """Returns an ContextWebInformation object that specifies metadata about the site""" request = RequestOptions("contextInfo") request.method = HttpMethod.Post if request_options: request.proxies = request_options.proxies request.verify = request_options.verify response = self.execute_request_direct(request) json = response.json() json_format = JsonLightFormat() json_format.function_tag_name = "GetContextWebInformation" return_value = ContextWebInformation() self.pending_request().map_json(json, return_value, json_format) return return_value def get_context_web_information_ex(self): """Returns an ContextWebInformation object that specifies metadata about the site""" return_type = ClientResult(self, ContextWebInformation()) def _construct_request(request): """ :type request: office365.runtime.http.request_options.RequestOptions """ request.url = self.service_root_url() + "/contextInfo" qry = ServiceOperationQuery(self.web, "GetContextWebInformation", None, None, None, return_type) self.before_execute(_construct_request) self.add_query(qry) return return_type def execute_query_with_incremental_retry(self, max_retry=5): """Handles throttling requests.""" settings = {"timeout": 0} def _try_process_if_failed(retry, ex): """ :type retry: int :type ex: requests.exceptions.RequestException """ # check if request was throttled - http status code 429 # or check is request failed due to server unavailable - http status code 503 if ex.response.status_code == 429 or ex.response.status_code == 503: retry_after = ex.response.headers.get("Retry-After", None) if retry_after is not None: settings["timeout"] = int(retry_after) self.execute_query_retry(timeout_secs=settings.get("timeout"), max_retry=max_retry, failure_callback=_try_process_if_failed) def clone(self, url, clear_queries=True): """ Creates a clone of ClientContext :param bool clear_queries: :param str url: Site Url :return ClientContext """ ctx = copy.deepcopy(self) ctx._base_url = url ctx._ctx_web_info = None if clear_queries: ctx.clear() return ctx def authenticate_request(self, request): self._auth_context.authenticate_request(request) def _build_modification_query(self, request): """ Constructs SharePoint specific modification OData request :type request: RequestOptions """ query = self.current_query if request.method == HttpMethod.Post: self.ensure_form_digest(request) # set custom SharePoint control headers if isinstance(self.pending_request().default_json_format, JsonLightFormat): if isinstance(query, DeleteEntityQuery): request.ensure_header("X-HTTP-Method", "DELETE") request.ensure_header("IF-MATCH", '*') elif isinstance(query, UpdateEntityQuery): request.ensure_header("X-HTTP-Method", "MERGE") request.ensure_header("IF-MATCH", '*') @property def context_info(self): """Returns an ContextWebInformation object that specifies metadata about the site :rtype: ContextWebInformation """ return self._ctx_web_info @property def web(self): """Get Web client object""" if not self._web: self._web = Web(self) return self._web @property def site(self): """Get Site client object""" if not self._site: self._site = Site(self) return self._site @property def me(self): """Gets the user context for the present request""" return RequestUserContext(self, ResourcePath("Me")) @property def micro_service_manager(self): """Alias to MicroServiceManager""" from office365.sharepoint.microservice.manager import MicroServiceManager return MicroServiceManager(self, ResourcePath("microServiceManager")) @property def group_site_manager(self): """Alias to GroupSiteManager""" from office365.sharepoint.portal.group_site_manager import GroupSiteManager return GroupSiteManager(self, ResourcePath("groupSiteManager")) @property def group_service(self): """Alias to GroupService""" from office365.sharepoint.portal.group_service import GroupService return GroupService(self, ResourcePath("GroupService")) @property def people_manager(self): """Alias to PeopleManager""" from office365.sharepoint.userprofiles.people_manager import PeopleManager return PeopleManager(self) @property def profile_loader(self): """Alias to ProfileLoader""" from office365.sharepoint.userprofiles.profile_loader import ProfileLoader return ProfileLoader.get_profile_loader(self) @property def lists(self): """Alias to ListCollection. Gets information about all lists that the current user can access.""" from office365.sharepoint.lists.collection import ListCollection return ListCollection(self, ResourcePath("Lists")) @property def hub_sites(self): """Alias to HubSiteCollection. Gets information about all hub sites that the current user can access.""" return HubSiteCollection(self, ResourcePath("hubSites")) @property def site_pages(self): """Alias to SitePageService. Represents a set of APIs to use for managing site pages.""" return SitePageService(self, ResourcePath("sitePages")) @property def site_icon_manager(self): """Alias to Microsoft.SharePoint.Portal.SiteIconManager. """ from office365.sharepoint.portal.site_icon_manager import SiteIconManager return SiteIconManager(self, ResourcePath("SiteIconManager")) @property def site_linking_manager(self): """Alias to Microsoft.SharePoint.Portal.SiteLinkingManager. """ from office365.sharepoint.portal.site_linking_manager import SiteLinkingManager return SiteLinkingManager(self, ResourcePath("siteLinkingManager")) @property def site_manager(self): """Alias to SPSiteManager. Represents methods for creating and managing SharePoint sites""" from office365.sharepoint.portal.site_manager import SPSiteManager return SPSiteManager(self, ResourcePath("spSiteManager")) @property def theme_manager(self): """Alias to SP.Utilities.ThemeManager. Represents methods for creating and managing site theming""" from office365.sharepoint.portal.theme_manager import ThemeManager return ThemeManager(self, ResourcePath("themeManager")) @property def taxonomy(self): """Alias to TaxonomyService""" from office365.sharepoint.taxonomy.service import TaxonomyService return TaxonomyService(self) @property def search(self): """Alias to SearchService""" from office365.sharepoint.search.service import SearchService return SearchService(self) @property def base_url(self): """Represents absolute Web or Site Url""" return self._base_url @property def authentication_context(self): return self._auth_context def service_root_url(self): return "{0}/_api".format(self.base_url)
class ClientContext(ClientRuntimeContext): """SharePoint client context""" def __init__(self, base_url, auth_context=None): """ :type base_url: str :type auth_context: AuthenticationContext or None """ if base_url.endswith("/"): base_url = base_url[:len(base_url) - 1] if auth_context is None: self._auth_context = AuthenticationContext(url=base_url) else: self._auth_context = auth_context super(ClientContext, self).__init__() self.__web = None self.__site = None self._base_url = base_url self._contextWebInformation = None self._pendingRequest = ODataRequest(self, JsonLightFormat(ODataMetadataLevel.Verbose)) self._pendingRequest.beforeExecute += self._build_modification_query @staticmethod def from_url(abs_url): """ Constructs ClientContext from absolute Url :param str abs_url: Absolute Url to resource :return: ClientContext """ base_url = resolve_base_url(abs_url) ctx = ClientContext(base_url) result = Web.get_web_url_from_page_url(ctx, abs_url) def _init_context_for_web(resp): """ :type resp: requests.Response """ resp.raise_for_status() ctx._base_url = result.value ctx.after_execute(_init_context_for_web) return ctx def with_client_certificate(self, tenant, client_id, thumbprint, cert_path): """Creates authenticated SharePoint context via certificate credentials :param str tenant: Tenant name :param str cert_path: Path to A PEM encoded certificate private key. :param str thumbprint: Hex encoded thumbprint of the certificate. :param str client_id: The OAuth client id of the calling application. """ self.authentication_context.with_client_certificate(tenant, client_id, thumbprint, cert_path) return self def with_access_token(self, token_func): """ :type token_func: () -> TokenResponse """ self.authentication_context.register_provider(token_func) return self def with_user_credentials(self, username, password, allow_ntlm=False, browser_mode=False): """ Assigns credentials :type username: str :type password: str :type allow_ntlm: bool :type browser_mode: bool """ self.authentication_context.register_provider( UserCredential(username, password), allow_ntlm=allow_ntlm, browser_mode=browser_mode) return self def with_credentials(self, credentials): """ Assigns credentials :type credentials: UserCredential or ClientCredential """ self.authentication_context.register_provider(credentials) return self def execute_batch(self, items_per_batch=100): """ Construct and submit a batch request :param int items_per_batch: Maximum to be selected for bulk operation """ batch_request = ODataBatchRequest(self) def _prepare_batch_request(request): self.ensure_form_digest(request) batch_request.beforeExecute += _prepare_batch_request all_queries = [qry for qry in self.pending_request().next_query()] for i in range_or_xrange(0, len(all_queries), items_per_batch): queries = all_queries[i:i + items_per_batch] batch_request.add_query(BatchQuery(self, queries)) batch_request.execute_query() def build_single_request(self, query): """ :type: office365.runtime.queries.client_query.ClientQuery """ request = super(ClientContext, self).build_single_request(query) self._build_modification_query(request) return request def pending_request(self): """ :return: ODataRequest """ return self._pendingRequest def ensure_form_digest(self, request_options): """ :type request_options: RequestOptions """ if not self._contextWebInformation: self._contextWebInformation = ContextWebInformation() self.request_form_digest() request_options.set_header('X-RequestDigest', self._contextWebInformation.FormDigestValue) def request_form_digest(self): """Request Form Digest""" request = RequestOptions("contextInfo") request.method = HttpMethod.Post response = self.execute_request_direct(request) json = response.json() json_format = JsonLightFormat() json_format.function_tag_name = "GetContextWebInformation" self.pending_request().map_json(json, self._contextWebInformation, json_format) def clone(self, url, clear_queries=True): """ Creates a clone of ClientContext :param bool clear_queries: :param str url: Site Url :return ClientContext """ ctx = copy.deepcopy(self) ctx._base_url = url if clear_queries: ctx.clear_queries() return ctx def authenticate_request(self, request): self._auth_context.authenticate_request(request) def _build_modification_query(self, request): """ Constructs SharePoint specific modification OData request :type request: RequestOptions """ query = self.current_query if request.method == HttpMethod.Post: self.ensure_form_digest(request) # set custom SharePoint control headers if isinstance(self._pendingRequest.json_format, JsonLightFormat): if isinstance(query, DeleteEntityQuery): request.ensure_header("X-HTTP-Method", "DELETE") request.ensure_header("IF-MATCH", '*') elif isinstance(query, UpdateEntityQuery): request.ensure_header("X-HTTP-Method", "MERGE") request.ensure_header("IF-MATCH", '*') @property def web(self): """Get Web client object""" if not self.__web: self.__web = Web(self) return self.__web @property def site(self): """Get Site client object""" if not self.__site: self.__site = Site(self) return self.__site @property def base_url(self): return self._base_url @property def authentication_context(self): return self._auth_context def service_root_url(self): return "{0}/_api".format(self.base_url)