From c1c875dd0e8d2259b88fbc58ff797af39121e702 Mon Sep 17 00:00:00 2001 From: vgrem Date: Fri, 27 Oct 2023 23:18:01 +0300 Subject: [PATCH] typings improvements for SharePoint API, changes namespace improvements, examples updates --- examples/outlook/messages/get_basic_props.py | 2 +- .../sharepoint/listitems/versions/__init__.py | 0 .../sharepoint/search/search_sort_list.py | 7 ++ .../sites/create_comm_site_with_owner.py | 2 + examples/sharepoint/sites/create_team_site.py | 2 +- examples/sharepoint/webs/get_all.py | 3 + examples/sharepoint/webs/get_changes.py | 10 +++ office365/sharepoint/changes/change.py | 22 +++++- office365/sharepoint/changes/collection.py | 2 +- office365/sharepoint/changes/list.py | 3 + office365/sharepoint/changes/type.py | 46 ++++++------ office365/sharepoint/changes/web.py | 10 ++- office365/sharepoint/client_context.py | 39 +++++----- office365/sharepoint/fields/field.py | 71 +++++++------------ office365/sharepoint/folders/collection.py | 6 +- office365/sharepoint/folders/folder.py | 6 +- office365/sharepoint/forms/form.py | 8 +-- .../listitems/versions/collection.py | 2 +- .../sharepoint/listitems/versions/version.py | 29 +++++--- office365/sharepoint/sites/site.py | 7 +- .../tenant/administration/tenant.py | 55 +++++++------- .../sharepoint/webs/template_collection.py | 2 +- office365/sharepoint/webs/web.py | 7 +- tests/sharepoint/test_change.py | 8 +-- tests/sharepoint/test_search.py | 8 ++- 25 files changed, 198 insertions(+), 159 deletions(-) delete mode 100644 examples/sharepoint/listitems/versions/__init__.py create mode 100644 examples/sharepoint/webs/get_changes.py diff --git a/examples/outlook/messages/get_basic_props.py b/examples/outlook/messages/get_basic_props.py index 26b22a8bf..eb456d0ca 100644 --- a/examples/outlook/messages/get_basic_props.py +++ b/examples/outlook/messages/get_basic_props.py @@ -12,4 +12,4 @@ user = client.users[test_user_principal_name] messages = user.messages.select(["id", "subject"]).top(10).get().execute_query() for message in messages: # type: Message - print(message.id) + print(message.subject) diff --git a/examples/sharepoint/listitems/versions/__init__.py b/examples/sharepoint/listitems/versions/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/sharepoint/search/search_sort_list.py b/examples/sharepoint/search/search_sort_list.py index 9b632c053..427106615 100644 --- a/examples/sharepoint/search/search_sort_list.py +++ b/examples/sharepoint/search/search_sort_list.py @@ -1,3 +1,9 @@ +""" +Demonstrates how to use the Search REST service in SharePoint + +https://learn.microsoft.com/en-us/sharepoint/dev/general-development/sharepoint-search-rest-api-overview +""" + from office365.sharepoint.client_context import ClientContext from office365.sharepoint.search.query.sort.sort import Sort from tests import test_site_url, test_user_credentials @@ -9,6 +15,7 @@ select_properties=["Path", "LastModifiedTime"], row_limit=20, ).execute_query() + results = result.value.PrimaryQueryResult.RelevantResults for row in results.Table.Rows: print(row.Cells["Path"], row.Cells["LastModifiedTime"]) diff --git a/examples/sharepoint/sites/create_comm_site_with_owner.py b/examples/sharepoint/sites/create_comm_site_with_owner.py index 17e1ae407..dfcd750ee 100644 --- a/examples/sharepoint/sites/create_comm_site_with_owner.py +++ b/examples/sharepoint/sites/create_comm_site_with_owner.py @@ -1,5 +1,7 @@ """ Creates a modern site + +https://learn.microsoft.com/en-us/sharepoint/dev/apis/site-creation-rest#create-a-modern-site """ from office365.sharepoint.client_context import ClientContext from tests import ( diff --git a/examples/sharepoint/sites/create_team_site.py b/examples/sharepoint/sites/create_team_site.py index c064a4970..8456b6e77 100644 --- a/examples/sharepoint/sites/create_team_site.py +++ b/examples/sharepoint/sites/create_team_site.py @@ -1,5 +1,5 @@ """ -Creates Team site +Creates a Team site """ from office365.sharepoint.client_context import ClientContext diff --git a/examples/sharepoint/webs/get_all.py b/examples/sharepoint/webs/get_all.py index 01fa55f48..f3aca07f3 100644 --- a/examples/sharepoint/webs/get_all.py +++ b/examples/sharepoint/webs/get_all.py @@ -1,3 +1,6 @@ +""" + +""" from office365.sharepoint.client_context import ClientContext from tests import test_client_credentials, test_site_url diff --git a/examples/sharepoint/webs/get_changes.py b/examples/sharepoint/webs/get_changes.py new file mode 100644 index 000000000..2fa112049 --- /dev/null +++ b/examples/sharepoint/webs/get_changes.py @@ -0,0 +1,10 @@ +""" +Returns the collection of all changes from the change log that have occurred within the scope of the site +""" +from office365.sharepoint.client_context import ClientContext +from tests import test_client_credentials, test_team_site_url + +client = ClientContext(test_team_site_url).with_credentials(test_client_credentials) +changes = client.web.get_changes().execute_query() +for change in changes: + print(change) diff --git a/office365/sharepoint/changes/change.py b/office365/sharepoint/changes/change.py index af92e7893..12a874a3e 100644 --- a/office365/sharepoint/changes/change.py +++ b/office365/sharepoint/changes/change.py @@ -1,10 +1,28 @@ +import datetime +import inspect +from typing import Optional + from office365.sharepoint.changes.token import ChangeToken +from office365.sharepoint.changes.type import ChangeType from office365.sharepoint.entity import Entity class Change(Entity): """Base class for a change. installation.""" + @property + def change_type_name(self): + return next( + iter( + [ + item[0] + for item in inspect.getmembers(ChangeType) + if item[1] == self.change_type + ] + ), + None, + ) + @property def change_token(self): """ @@ -22,6 +40,7 @@ def change_type(self): @property def site_id(self): + # type: () -> Optional[str] """ Returns the Id of the site of the changed item """ @@ -29,10 +48,11 @@ def site_id(self): @property def time(self): + # type: () -> datetime.datetime """ Gets a value that specifies the time that the object was modified. """ - return self.properties.get("Time", None) + return self.properties.get("Time", datetime.datetime.min) def get_property(self, name, default_value=None): if default_value is None: diff --git a/office365/sharepoint/changes/collection.py b/office365/sharepoint/changes/collection.py index 8e1543523..1fffa7a31 100644 --- a/office365/sharepoint/changes/collection.py +++ b/office365/sharepoint/changes/collection.py @@ -2,7 +2,7 @@ from office365.sharepoint.entity_collection import EntityCollection -class ChangeCollection(EntityCollection): +class ChangeCollection(EntityCollection[Change]): """Represents a collection of Change objects""" def __init__(self, context, resource_path=None): diff --git a/office365/sharepoint/changes/list.py b/office365/sharepoint/changes/list.py index 93b99ebd3..ac684b1fc 100644 --- a/office365/sharepoint/changes/list.py +++ b/office365/sharepoint/changes/list.py @@ -9,6 +9,9 @@ class ChangeList(Change): The RelativeTime and RootFolderUrl properties are not included in the default scalar property set for this type. """ + def __repr__(self): + return "List: {0}".format(self.list_id) + @property def base_template(self): """An SP.ListTemplateType object that returns the list template type of the list.""" diff --git a/office365/sharepoint/changes/type.py b/office365/sharepoint/changes/type.py index c5126f176..7200a1f48 100644 --- a/office365/sharepoint/changes/type.py +++ b/office365/sharepoint/changes/type.py @@ -4,78 +4,78 @@ class ChangeType: def __init__(self): pass - NoChange = "NoChange" + NoChange = 0 """Indicates that no change has taken place.""" - Add = "Add" + Add = 1 """Specifies that an object has been added within the scope of a list, site, site collection, or content database""" - Update = "Update" + Update = 2 """Specifies that an object has been modified within the scope of a list, site, site collection, or content database.""" - DeleteObject = "DeleteObject" + DeleteObject = 3 """ Specifies that an object has been deleted within the scope of a list, site, site collection, or content database """ - Rename = "Rename" + Rename = 4 """The leaf in a URL has been renamed.""" - MoveAway = "MoveAway" + MoveAway = 5 """Specifies that a non-leaf segment within a URL has been renamed. The object was moved away from the location within the site specified by the change.""" - MoveInto = "MoveInto" + MoveInto = 6 """Specifies that a non-leaf segment within a URL has been renamed. The object was moved into the location within the site specified by the change.""" - Restore = "Restore" + Restore = 7 """Specifies that an object (1) has restored from a backup or from the Recycle Bin""" - RoleAdd = "RoleAdd" + RoleAdd = 8 """Specifies that a role definition has been added.""" - RoleDelete = "RoleDelete" + RoleDelete = 9 """Specifies that a role definition has been deleted.""" - RoleUpdate = "RoleUpdate" + RoleUpdate = 10 """Specifies that a role definition has been updated.""" - AssignmentAdd = "AssignmentAdd" + AssignmentAdd = 11 """Specifies that a user has been given permissions to a list. The list MUST have different permissions from its parent.""" - AssignmentDelete = "AssignmentDelete" + AssignmentDelete = 12 """Specifies that a user has lost permissions to a list. The list MUST have different permissions from its parent""" - MemberAdd = "MemberAdd" + MemberAdd = 13 """Specifies that a user has been added to a group.""" - MemberDelete = "MemberDelete" + MemberDelete = 14 """Specifies that a user has been removed from a group.""" - SystemUpdate = "SystemUpdate" + SystemUpdate = 15 """Specifies that a change has been made to an item by using the protocol server method.""" - Navigation = "Navigation" + Navigation = 16 """Specifies that a change in the navigation structure of a site collection has been made.""" - ScopeAdd = "ScopeAdd" + ScopeAdd = 17 """Specifies that a change in permissions scope has been made to break inheritance from the parent of an object """ - ScopeDelete = "ScopeDelete" + ScopeDelete = 18 """Specifies that a change in permissions scope has been made to revert back to inheriting permissions from the parent of an object""" - ListContentTypeAdd = "ListContentTypeAdd" + ListContentTypeAdd = 19 """Specifies that a list content type has been added.""" - ListContentTypeDelete = "ListContentTypeDelete" + ListContentTypeDelete = 20 """Specifies that a list content type has been deleted.""" - Dirty = "Dirty" + Dirty = 21 """Specifies that this item has a pending modification due to an operation on another item.""" - Activity = "Activity" + Activity = 22 """Specifies that an activity change as specified in section 3.2.5.462 has been made to the object """ diff --git a/office365/sharepoint/changes/web.py b/office365/sharepoint/changes/web.py index 34bb35af6..9e7c6e5d1 100644 --- a/office365/sharepoint/changes/web.py +++ b/office365/sharepoint/changes/web.py @@ -1,14 +1,18 @@ +from typing import Optional + from office365.sharepoint.changes.change import Change class ChangeWeb(Change): """Specifies a change on a site""" + def __repr__(self): + return "Web: {0}, Action: {1}".format(self.web_id, self.change_type_name) + @property def web_id(self): + # type: () -> Optional[str] """ - Identifies the site (2) that has changed - - :rtype: str or None + Identifies the site that has changed """ return self.properties.get("WebId", None) diff --git a/office365/sharepoint/client_context.py b/office365/sharepoint/client_context.py index 562a9cc90..35ee766f4 100644 --- a/office365/sharepoint/client_context.py +++ b/office365/sharepoint/client_context.py @@ -8,6 +8,7 @@ from office365.runtime.auth.client_credential import ClientCredential from office365.runtime.auth.token_response import TokenResponse from office365.runtime.auth.user_credential import UserCredential +from office365.runtime.client_result import ClientResult from office365.runtime.client_runtime_context import ClientRuntimeContext from office365.runtime.compat import get_absolute_url, urlparse from office365.runtime.http.http_method import HttpMethod @@ -19,8 +20,14 @@ from office365.runtime.queries.delete_entity import DeleteEntityQuery from office365.runtime.queries.update_entity import UpdateEntityQuery from office365.runtime.types.event_handler import EventHandler +from office365.sharepoint.portal.groups.site_info import GroupSiteInfo +from office365.sharepoint.portal.sites.creation_response import SPSiteCreationResponse from office365.sharepoint.portal.sites.status import SiteStatus +from office365.sharepoint.principal.users.user import User from office365.sharepoint.publishing.pages.service import SitePageService +from office365.sharepoint.publishing.sites.communication.creation_response import ( + CommunicationSiteCreationResponse, +) from office365.sharepoint.request_user_context import RequestUserContext from office365.sharepoint.sites.site import Site from office365.sharepoint.tenant.administration.hubsites.collection import ( @@ -279,8 +286,10 @@ def _build_modification_query(self, request): request.ensure_header("IF-MATCH", "*") def create_modern_site(self, title, alias, owner=None): + # type: (str, str, Optional[str | User]) -> Site """ - Creates a modern site + Creates a modern (Communication) site + https://learn.microsoft.com/en-us/sharepoint/dev/apis/site-creation-rest#create-a-modern-site :param str alias: Site alias which defines site url, e.g. https://contoso.sharepoint.com/sites/{alias} :param str title: Site title @@ -290,19 +299,17 @@ def create_modern_site(self, title, alias, owner=None): site_url = "{base_url}/sites/{alias}".format( base_url=get_absolute_url(self.base_url), alias=alias ) - result = self.site_manager.create(title, site_url, owner) - def _after_site_create(resp): - """ - :type resp: requests.Response - """ - resp.raise_for_status() + def _after_site_create(result): + # type: (ClientResult[SPSiteCreationResponse]) -> None if result.value.SiteStatus == SiteStatus.Error: raise ValueError(result.value.ErrorMessage) elif result.value.SiteStatus == SiteStatus.Ready: return_type.set_property("__siteUrl", result.value.SiteUrl) - self.after_execute(_after_site_create) + self.site_manager.create(title, site_url, owner).after_execute( + _after_site_create + ) return return_type def create_team_site(self, alias, title, is_public=True): @@ -312,20 +319,18 @@ def create_team_site(self, alias, title, is_public=True): :param str title: Site title :param bool is_public: """ - result = self.group_site_manager.create_group_ex(title, alias, is_public) return_type = Site(self) - def _after_site_created(resp): - """ - :type resp: requests.Response - """ - resp.raise_for_status() + def _after_site_created(result): + # type: (ClientResult[GroupSiteInfo]) -> None if result.value.SiteStatus == SiteStatus.Error: raise ValueError(result.value.ErrorMessage) elif result.value.SiteStatus == SiteStatus.Ready: return_type.set_property("__siteUrl", result.value.SiteUrl) - self.after_execute(_after_site_created) + self.group_site_manager.create_group_ex(title, alias, is_public).after_execute( + _after_site_created + ) return return_type def create_communication_site(self, alias, title): @@ -341,9 +346,7 @@ def create_communication_site(self, alias, title): ) def _after_site_created(result): - """ - :type result: ClientResult - """ + # type: (ClientResult[CommunicationSiteCreationResponse]) -> None if result.value.SiteStatus == SiteStatus.Error: raise ValueError("Site creation error") elif result.value.SiteStatus == SiteStatus.Ready: diff --git a/office365/sharepoint/fields/field.py b/office365/sharepoint/fields/field.py index 927c49dae..6eb380403 100644 --- a/office365/sharepoint/fields/field.py +++ b/office365/sharepoint/fields/field.py @@ -138,9 +138,7 @@ def auto_indexed(self): @property def default_formula(self): - """ - :rtype: str or None - """ + # type: () -> Optional[str] return self.properties.get("DefaultFormula", None) @property @@ -155,9 +153,9 @@ def description_resource(self): @property def schema_xml(self): - """Gets a value that specifies the XML schema that defines the field. - - :rtype: str or None + # type: () -> Optional[str] + """ + Gets a value that specifies the XML schema that defines the field. """ return self.properties.get("SchemaXml", None) @@ -168,41 +166,32 @@ def schema_xml(self, val): @property def type_as_string(self): - """Gets a value that specifies the type of the field.. - - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets a value that specifies the type of the field""" return self.properties.get("TypeAsString", None) @property def title(self): - """Gets a value that specifies the display name of the field. - - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets a value that specifies the display name of the field.""" return self.properties.get("Title", None) @title.setter def title(self, val): """ Sets a value that specifies the display name of the field. - :rtype: str or None """ self.set_property("Title", val) @property def group(self): - """ - Gets a value that specifies the field group. - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets a value that specifies the field group""" return self.properties.get("Group", None) @group.setter def group(self, val): - """Sets a value that specifies the field group. - :rtype: str or None - """ + """Sets a value that specifies the field group.""" self.set_property("Group", val) @property @@ -215,71 +204,65 @@ def internal_name(self): @property def can_be_deleted(self): + # type: () -> Optional[bool] """ Gets a value that specifies whether the field can be deleted - :rtype: bool or None """ return self.properties.get("CanBeDeleted", None) @property def client_side_component_id(self): - """ - :rtype: str or None - """ + # type: () -> Optional[str] return self.properties.get("ClientSideComponentId", None) @property def client_side_component_properties(self): - """ - :rtype: str or None - """ + # type: () -> Optional[str] return self.properties.get("ClientSideComponentProperties", None) @property def client_validation_formula(self): - """ - :rtype: str or None - """ + # type: () -> Optional[str] return self.properties.get("ClientValidationFormula", None) @property def enforce_unique_values(self): + # type: () -> Optional[bool] """ Specifies whether the field enforces unique values. - :rtype: bool or None """ return self.properties.get("enforceUniqueValues", None) @property def filterable(self): + # type: () -> Optional[bool] """ Specifies whether list items in the list can be filtered by the field value. - :rtype: bool or None """ return self.properties.get("Filterable", None) @property def from_base_type(self): + # type: () -> Optional[bool] """ Gets a Boolean value that indicates whether the field derives from a base field type. - :rtype: bool or None """ return self.properties.get("FromBaseType", None) @property def js_link(self): + # type: () -> Optional[str] """ When implemented in a derived class, gets or sets the name of an external JavaScript file that contains any client rendering logic for fields of the derived type. - :rtype: str or None """ return self.properties.get("JSLink", None) @property def hidden(self): + # type: () -> Optional[bool] """ Gets a value that specifies whether the field is hidden in list views and list forms. - :rtype: bool or None """ return self.properties.get("Hidden", None) @@ -292,25 +275,25 @@ def hidden(self, val): @property def no_crawl(self): + # type: () -> Optional[bool] """ Gets value that specifies whether the field can be crawled by a search engine. - :rtype: bool or None """ return self.properties.get("NoCrawl", None) @property def read_only_field(self): + # type: () -> Optional[bool] """ Specifies whether the value of the field is read-only. - :rtype: bool or None """ return self.properties.get("ReadOnlyField", None) @property def default_value(self): + # type: () -> Optional[str] """ Gets a value that specifies the default value for the field. - :rtype: str or None """ return self.properties.get("DefaultValue", None) @@ -323,16 +306,13 @@ def default_value(self, val): @property def indexed(self): - """ - :rtype: str or None - """ return self.properties.get("Indexed", None) @property def type_display_name(self): + # type: () -> Optional[str] """ Gets a value that specifies the display name for the type of the field. - :rtype: str or None """ return self.properties.get("TypeDisplayName", None) @@ -348,10 +328,9 @@ def title_resource(self): @property def type_short_description(self): + # type: () -> Optional[str] """ Gets a value that specifies the description for the type of the field. - - :rtype: str or None """ return self.properties.get("TypeShortDescription", None) diff --git a/office365/sharepoint/folders/collection.py b/office365/sharepoint/folders/collection.py index a6f1871f2..571c99a10 100644 --- a/office365/sharepoint/folders/collection.py +++ b/office365/sharepoint/folders/collection.py @@ -20,12 +20,12 @@ def add_using_path(self, decoded_url, overwrite): :param bool overwrite: bool """ parameters = {"DecodedUrl": decoded_url, "Overwrite": overwrite} - target_folder = Folder(self.context) + return_type = Folder(self.context) qry = ServiceOperationQuery( - self, "AddUsingPath", parameters, None, None, target_folder + self, "AddUsingPath", parameters, None, None, return_type ) self.context.add_query(qry) - return target_folder + return return_type def ensure_path(self, path): """ diff --git a/office365/sharepoint/folders/folder.py b/office365/sharepoint/folders/folder.py index eeb225fff..6f405eb41 100644 --- a/office365/sharepoint/folders/folder.py +++ b/office365/sharepoint/folders/folder.py @@ -265,10 +265,8 @@ def add(self, name): return self.folders.add(name) def rename(self, name): - """Rename a Folder resource - - :type name: str - """ + # type: (str) -> Self + """Rename a Folder resource""" item = self.list_item_all_fields item.set_property("Title", name) item.set_property("FileLeafRef", name) diff --git a/office365/sharepoint/forms/form.py b/office365/sharepoint/forms/form.py index e5d70aaf3..065d172d4 100644 --- a/office365/sharepoint/forms/form.py +++ b/office365/sharepoint/forms/form.py @@ -1,3 +1,5 @@ +from typing import Optional + from office365.sharepoint.entity import Entity from office365.sharepoint.types.resource_path import ResourcePath as SPResPath @@ -7,19 +9,17 @@ class Form(Entity): @property def form_type(self): + # type: () -> Optional[str] """ Gets the type of the form. - - :rtype: str or None """ return self.properties.get("FormType", None) @property def server_relative_url(self): + # type: () -> Optional[str] """ Gets the server-relative URL of the form. - - :rtype: str or None """ return self.properties.get("ServerRelativeUrl", None) diff --git a/office365/sharepoint/listitems/versions/collection.py b/office365/sharepoint/listitems/versions/collection.py index abae58202..ce6b94d7b 100644 --- a/office365/sharepoint/listitems/versions/collection.py +++ b/office365/sharepoint/listitems/versions/collection.py @@ -7,7 +7,7 @@ from office365.sharepoint.listitems.versions.version import ListItemVersion -class ListItemVersionCollection(EntityCollection): +class ListItemVersionCollection(EntityCollection[ListItemVersion]): """Specifies a collection of versions of a list item.""" def __init__(self, context, resource_path=None): diff --git a/office365/sharepoint/listitems/versions/version.py b/office365/sharepoint/listitems/versions/version.py index a5b12549e..9b8af6b54 100644 --- a/office365/sharepoint/listitems/versions/version.py +++ b/office365/sharepoint/listitems/versions/version.py @@ -1,30 +1,39 @@ import datetime +from typing import TYPE_CHECKING, Optional from office365.runtime.paths.resource_path import ResourcePath -from office365.runtime.paths.service_operation import ServiceOperationPath from office365.sharepoint.entity import Entity +if TYPE_CHECKING: + from office365.sharepoint.listitems.versions.collection import ( + ListItemVersionCollection, + ) + class ListItemVersion(Entity): """Represents a version of a list item.""" + def __repr__(self): + return "Label: {0}, Url: {1}".format( + self.version_label, self.properties.get("FileRef", None) + ) + @property def version_id(self): + # type: () -> Optional[int] """Gets the ID of the version.""" - return int(self.properties.get("VersionId", -1)) + return int(self.properties.get("VersionId", None)) @property def version_label(self): - """Gets the version number of the item version. - :rtype: str or None - """ + # type: () -> Optional[str] + """Gets the version number of the item version.""" return self.properties.get("VersionLabel", None) @property def is_current_version(self): - """Gets a value that specifies whether the file version is the current version. - :rtype: bool - """ + # type: () -> Optional[bool] + """Gets a value that specifies whether the file version is the current version.""" return self.properties.get("IsCurrentVersion", None) @property @@ -68,9 +77,7 @@ def property_ref_name(self): @property def parent_collection(self): - """ - :rtype: office365.sharepoint.listitems.versions.collection.ListItemVersionCollection - """ + # type: () -> ListItemVersionCollection return self._parent_collection def get_property(self, name, default_value=None): diff --git a/office365/sharepoint/sites/site.py b/office365/sharepoint/sites/site.py index 699a7fd87..6412feddd 100644 --- a/office365/sharepoint/sites/site.py +++ b/office365/sharepoint/sites/site.py @@ -8,6 +8,7 @@ from office365.runtime.queries.service_operation import ServiceOperationQuery from office365.sharepoint.audit.audit import Audit from office365.sharepoint.changes.collection import ChangeCollection +from office365.sharepoint.changes.query import ChangeQuery from office365.sharepoint.changes.token import ChangeToken from office365.sharepoint.compliance.store_proxy import SPPolicyStoreProxy from office365.sharepoint.compliance.tag import ComplianceTag @@ -189,12 +190,14 @@ def _site_loaded(): self.ensure_property("Url", _site_loaded) return return_type - def get_changes(self, query): + def get_changes(self, query=None): """Returns the collection of all changes from the change log that have occurred within the scope of the site, based on the specified query. :param office365.sharepoint.changes.query.ChangeQuery query: Specifies which changes to return """ + if query is None: + query = ChangeQuery(site=True, fetch_limit=100) return_type = ChangeCollection(self.context) payload = {"query": query} qry = ServiceOperationQuery( @@ -237,7 +240,7 @@ def _site_loaded(): def get_web_templates(self, lcid=1033, override_compat_level=0): """ Returns the collection of site definitions that are available for creating - Web sites within the site collection.<99> + Web sites within the site collection. :param int lcid: A 32-bit unsigned integer that specifies the language of the site definitions that are returned from the site collection. diff --git a/office365/sharepoint/tenant/administration/tenant.py b/office365/sharepoint/tenant/administration/tenant.py index 8e27a4047..fc8978391 100644 --- a/office365/sharepoint/tenant/administration/tenant.py +++ b/office365/sharepoint/tenant/administration/tenant.py @@ -334,6 +334,26 @@ def get_sites_by_state(self, states=None): self.context.add_query(qry) return return_type + def _poll_site_status(self, site_url, polling_interval_secs): + states = [0, 1, 2] + time.sleep(polling_interval_secs) + + def _after(items): + completed = ( + len( + [ + item + for item in items + if item.properties.get("SiteUrl") == site_url + ] + ) + > 0 + ) + if not completed: + self._poll_site_status(site_url, polling_interval_secs) + + self.get_sites_by_state(states).after_execute(_after, execute_first=True) + def get_site_health_status(self, source_url): """ :type source_url: str @@ -408,7 +428,6 @@ def register_hub_site(self, site_url): Registers an existing site as a hub site. :param str site_url: - :return: """ return_type = HubSiteProperties(self.context) params = {"siteUrl": site_url} @@ -453,39 +472,19 @@ def create_site(self, url, owner, title=None): def create_site_sync(self, url, owner, title=None): """Creates a site collection - :param str title: Sets the new site’s title. + :param str title: Sets the new site’s title. :param str url: Sets the new site’s URL. :param str owner: Sets the login name of the owner of the new site. """ return_type = Site(self.context) - op = self.create_site(url, owner, title) - - def _verify_site_status(resp, items=None): - """ - :type resp: requests.Response - """ - resp.raise_for_status() - if items is None: - is_complete = op.is_complete - else: - is_complete = ( - len( - [ - item - for item in items - if item.properties.get("SiteUrl") == url - ] - ) - > 0 - ) - if not is_complete: - time.sleep(op.polling_interval_secs) - items = self.get_sites_by_state([0, 1, 2]) - self.context.after_execute(_verify_site_status, items=items) + return_type.set_property("__siteUrl", url) - return_type.set_property("__siteUrl", url) + def _ensure_status(op): + # type: (SpoOperation) -> None + if not op.is_complete: + self._poll_site_status(url, op.polling_interval_secs) - self.context.after_execute(_verify_site_status) + self.create_site(url, owner, title).after_execute(_ensure_status) return return_type def remove_site(self, site_url): diff --git a/office365/sharepoint/webs/template_collection.py b/office365/sharepoint/webs/template_collection.py index 1abc372b1..0198f7f70 100644 --- a/office365/sharepoint/webs/template_collection.py +++ b/office365/sharepoint/webs/template_collection.py @@ -3,7 +3,7 @@ from office365.sharepoint.webs.template import WebTemplate -class WebTemplateCollection(EntityCollection): +class WebTemplateCollection(EntityCollection[WebTemplate]): """Specifies a collection of site templates.""" def __init__(self, context, resource_path=None, parent=None): diff --git a/office365/sharepoint/webs/web.py b/office365/sharepoint/webs/web.py index 631cf8af9..7ff5d0322 100644 --- a/office365/sharepoint/webs/web.py +++ b/office365/sharepoint/webs/web.py @@ -16,6 +16,7 @@ ) from office365.sharepoint.businessdata.app_bdc_catalog import AppBdcCatalog from office365.sharepoint.changes.collection import ChangeCollection +from office365.sharepoint.changes.query import ChangeQuery from office365.sharepoint.clientsidecomponent.hostedapps.manager import ( HostedAppsManager, ) @@ -1019,12 +1020,14 @@ def get_list(self, path): ServiceOperationPath("getList", [str(safe_path)], self.resource_path), ) - def get_changes(self, query): - """Returns the collection of all changes from the change log that have occurred within the scope of the site, + def get_changes(self, query=None): + """Returns the collection of all changes from the change log that have occurred within the scope of the web, based on the specified query. :param office365.sharepoint.changes.query.ChangeQuery query: Specifies which changes to return """ + if query is None: + query = ChangeQuery(web=True, fetch_limit=100) return_type = ChangeCollection(self.context) payload = {"query": query} qry = ServiceOperationQuery( diff --git a/tests/sharepoint/test_change.py b/tests/sharepoint/test_change.py index 464bfc714..d8847953f 100644 --- a/tests/sharepoint/test_change.py +++ b/tests/sharepoint/test_change.py @@ -6,15 +6,11 @@ class TestChange(SPTestCase): def test_1_get_web_changes(self): - changes = self.client.site.root_web.get_changes( - query=ChangeQuery(web=True) - ).execute_query() + changes = self.client.site.root_web.get_changes().execute_query() self.assertIsInstance(changes, ChangeCollection) def test_2_get_site_changes(self): - changes = self.client.site.get_changes( - query=ChangeQuery(site=True, fetch_limit=100) - ).execute_query() + changes = self.client.site.get_changes().execute_query() self.assertIsInstance(changes, ChangeCollection) def test_3_get_list_item_changes_since_token(self): diff --git a/tests/sharepoint/test_search.py b/tests/sharepoint/test_search.py index 664b30978..43f0048c8 100644 --- a/tests/sharepoint/test_search.py +++ b/tests/sharepoint/test_search.py @@ -53,13 +53,15 @@ def test6_search_get_query_with_select(self): self.assertIsInstance(result.value.PrimaryQueryResult, QueryResult) def test7_search_get_query_with_sort_list(self): - result = self.client.search.query( - "guide.docx", enable_sorting=True, sort_list=[Sort("LastModifiedTime", 1)] + result = self.client.search.post_query( + query_text="guide.docx", + enable_sorting=True, + sort_list=[Sort("LastModifiedTime", 1)], ).execute_query() self.assertIsInstance(result.value.PrimaryQueryResult, QueryResult) def test8_search_suggest(self): - result = self.client.search.suggest("guide.docx").execute_query() + result = self.client.search.suggest("guide").execute_query() self.assertIsInstance(result.value, QuerySuggestionResults) # def test9_auto_completions(self):