Skip to content

Commit

Permalink
OneDrive API : fix for shared path addressing (#801)
Browse files Browse the repository at this point in the history
  • Loading branch information
vgrem committed Dec 24, 2023
1 parent 40ae03a commit dd08028
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 38 deletions.
2 changes: 1 addition & 1 deletion examples/onedrive/files/get_by_abs_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from tests import test_team_site_url
from tests.graph_case import acquire_token_by_username_password

file_abs_url = "{0}/Shared Documents/big_buck_bunny.mp4".format(test_team_site_url)
file_abs_url = "{0}/Shared Documents/Financial Sample.xlsx".format(test_team_site_url)

client = GraphClient(acquire_token_by_username_password)
file_item = client.shares.by_url(file_abs_url).drive_item.get().execute_query()
Expand Down
14 changes: 14 additions & 0 deletions examples/onedrive/shares/read_workbook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""
Get workbook
"""
from office365.graph_client import GraphClient
from tests import test_team_site_url
from tests.graph_case import acquire_token_by_username_password

file_abs_url = "{0}/Shared Documents/Financial Sample.xlsx".format(test_team_site_url)

client = GraphClient(acquire_token_by_username_password)
drive_item = client.shares.by_url(file_abs_url).drive_item.get().execute_query()
worksheets = drive_item.workbook.worksheets.get().execute_query()
for ws in worksheets:
print(ws)
Empty file removed examples/onenote/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion examples/onenote/create_notebook.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Demonstrates how to create a new OneNote notebook
https://learn.microsoft.com/en-us/graph/api/onenote-post-notebooks?view=graph-rest-1.0&tabs=http
https://learn.microsoft.com/en-us/graph/api/onenote-post-notebooks?view=graph-rest-1.0
"""

from office365.graph_client import GraphClient
Expand Down
13 changes: 5 additions & 8 deletions office365/directory/applications/template.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from office365.entity import Entity
from office365.runtime.client_result import ClientResult
from office365.runtime.queries.service_operation import ServiceOperationQuery
Expand Down Expand Up @@ -25,11 +27,8 @@ def instantiate(self, display_name):

@property
def display_name(self):
"""
The name of the application.
:rtype: str or None
"""
# type: () -> Optional[str]
"""The name of the application."""
return self.properties.get("displayName", None)

@property
Expand All @@ -44,9 +43,7 @@ def categories(self):

@property
def supported_provisioning_types(self):
"""
The list of provisioning modes supported by this application
"""
"""The list of provisioning modes supported by this application"""
return self.properties.get("supportedProvisioningTypes", StringCollection())

@property
Expand Down
7 changes: 4 additions & 3 deletions office365/directory/protection/riskyusers/history_item.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from office365.directory.protection.riskyusers.activity import RiskUserActivity
from office365.directory.protection.riskyusers.risky_user import RiskyUser

Expand All @@ -13,7 +15,6 @@ def activity(self):

@property
def initiated_by(self):
"""The ID of actor that does the operation.
:rtype: str
"""
# type: () -> Optional[str]
"""The ID of actor that does the operation."""
return self.properties.get("initiatedBy", None)
6 changes: 6 additions & 0 deletions office365/onedrive/driveitems/driveItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,12 @@ def subscriptions(self):
),
)

def set_property(self, name, value, persist_changes=True):
super(DriveItem, self).set_property(name, value, persist_changes)
if name == "parentReference":
self._resource_path.parent.patch(self.parent_reference.driveId)
return self

def get_property(self, name, default_value=None):
# type: (str, P_T) -> P_T
if default_value is None:
Expand Down
15 changes: 10 additions & 5 deletions office365/onedrive/internal/paths/shared.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import base64

from office365.runtime.paths.resource_path import ResourcePath
from office365.runtime.paths.v4.entity import EntityPath


def _url_to_shared_token(url):
"""
Converts url into shared token
:param str url:
"""
# type: (str) -> str
"""Converts url into shared token"""
value = base64.b64encode(url.encode("ascii")).decode("ascii")
if value.endswith("="):
value = value[:-1]
return "u!" + value.replace("/", "_").replace("+", "-")


class SharedPath(ResourcePath):
class SharedPath(EntityPath):
"""Shared token path"""

def patch(self, key):
self._key = "items"
self._parent = ResourcePath(key, ResourcePath("drives"))
self.__class__ = ResourcePath
return self

@property
def segment(self):
return _url_to_shared_token(self._key)
3 changes: 2 additions & 1 deletion office365/onedrive/shares/drive_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from office365.onedrive.permissions.permission import Permission
from office365.onedrive.sites.site import Site
from office365.runtime.paths.resource_path import ResourcePath
from office365.runtime.paths.v4.entity import EntityPath


class SharedDriveItem(BaseItem):
Expand Down Expand Up @@ -47,7 +48,7 @@ def drive_item(self):
"""Used to access the underlying driveItem"""
return self.properties.get(
"driveItem",
DriveItem(self.context, ResourcePath("driveItem", self.resource_path)),
DriveItem(self.context, EntityPath("driveItem", self.resource_path)),
)

@property
Expand Down
24 changes: 18 additions & 6 deletions office365/onedrive/workbooks/names/named_item.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
from typing import Optional

from office365.entity import Entity
from office365.onedrive.workbooks.ranges.range import WorkbookRange
from office365.runtime.paths.resource_path import ResourcePath
from office365.runtime.queries.function import FunctionQuery


class WorkbookNamedItem(Entity):
"""Represents a defined name for a range of cells or value. Names can be primitive named objects
(as seen in the type below), range object, reference to a range. This object can be used to obtain range
object associated with names."""

def range(self):
"""Returns the range object that is associated with the name. Throws an exception if the named item's type
isn't a range."""
return_type = WorkbookRange(
self.context, ResourcePath("range", self.resource_path)
)
qry = FunctionQuery(self, "range", return_type=return_type)
self.context.add_query(qry)
return return_type

@property
def name(self):
"""The name of the object. Read-only.
:rtype str or None
"""
# type: () -> Optional[str]
"""The name of the object."""
return self.properties.get("name", None)

@property
def comment(self):
"""Represents the comment associated with this name.
:rtype str or None
"""
# type: () -> Optional[str]
"""Represents the comment associated with this name."""
return self.properties.get("comment", None)

@property
Expand Down
55 changes: 53 additions & 2 deletions office365/onedrive/workbooks/ranges/range.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,55 @@
from typing import Optional
from typing import List, Optional

from office365.entity import Entity
from office365.onedrive.workbooks.ranges.format import WorkbookRangeFormat
from office365.onedrive.workbooks.ranges.sort import WorkbookRangeSort
from office365.onedrive.workbooks.ranges.view import WorkbookRangeView
from office365.runtime.paths.resource_path import ResourcePath
from office365.runtime.queries.function import FunctionQuery
from office365.runtime.queries.service_operation import ServiceOperationQuery


class WorkbookRange(Entity):
"""Range represents a set of one or more contiguous cells such as a cell, a row, a column, block of cells, etc."""

def cell(self, row, column):
"""
Gets the range object containing the single cell based on row and column numbers. The cell can be outside
the bounds of its parent range, so long as it's stays within the worksheet grid.
:param int row: Row number of the cell to be retrieved. Zero-indexed.
:param int column: Column number of the cell to be retrieved. Zero-indexed.
"""
return_type = WorkbookRange(self.context)
params = {"row": row, "column": column}
qry = FunctionQuery(self, "cell", params, return_type=return_type)
self.context.add_query(qry)
return return_type

def clear(self, apply_to=None):
"""Clear range values such as format, fill, and border.
:param str apply_to:
"""
payload = {"applyTo": apply_to}
qry = ServiceOperationQuery(self, "clear", parameters_type=payload)
self.context.add_query(qry)
return self

def insert(self, shift):
"""
Inserts a cell or a range of cells into the worksheet in place of this range, and shifts the other cells to
make space. Returns a new Range object at the now blank space.
:param str shift: Specifies which way to shift the cells. The possible values are: Down, Right.
"""
return_type = WorkbookRange(self.context)
payload = {"shift": shift}
qry = ServiceOperationQuery(
self, "insert", parameters_type=payload, return_type=return_type
)
self.context.add_query(qry)
return return_type

def visible_view(self):
""""""
"""Get the range visible from a filtered range."""
return_type = WorkbookRangeView(self.context)
qry = FunctionQuery(self, "visibleView", return_type=return_type)
self.context.add_query(qry)
Expand Down Expand Up @@ -63,6 +100,20 @@ def sort(self):
WorkbookRangeSort(self.context, ResourcePath("sort", self.resource_path)),
)

@property
def values(self):
# type: () -> List
"""Represents the raw values of the specified range. The data returned could be of type string, number,
or a boolean. Cell that contains an error returns the error string."""
return self.properties.get("values", None)

@property
def value_types(self):
# type: () -> List
"""Represents the type of data of each cell. The possible values are:
Unknown, Empty, String, Integer, Double, Boolean, Error."""
return self.properties.get("valueTypes", None)

@property
def worksheet(self):
"""The worksheet containing the current range"""
Expand Down
8 changes: 6 additions & 2 deletions office365/onedrive/workbooks/tables/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ class WorkbookTable(Entity):

def data_body_range(self):
"""Gets the range object associated with the data body of the table."""
return_type = WorkbookRange(self.context)
return_type = WorkbookRange(
self.context, ResourcePath("dataBodyRange", self.resource_path)
)
qry = FunctionQuery(self, "dataBodyRange", return_type=return_type)
self.context.add_query(qry)
return return_type

def range(self):
"""Get the range object associated with the entire table."""
return_type = WorkbookRange(self.context)
return_type = WorkbookRange(
self.context, ResourcePath("range", self.resource_path)
)
qry = FunctionQuery(self, "range", return_type=return_type)
self.context.add_query(qry)
return return_type
Expand Down
15 changes: 7 additions & 8 deletions office365/runtime/paths/v4/entity.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from typing import Optional

from typing_extensions import Self

from office365.runtime.paths.resource_path import ResourcePath


class EntityPath(ResourcePath):
def __init__(self, key=None, parent=None, collection=None):
"""
:param str or None key: Entity key
:param ResourcePath or None collection:
"""
# type: (Optional[str], Optional[ResourcePath], Optional[ResourcePath]) -> None
super(EntityPath, self).__init__(key, parent)
self._collection = collection

Expand All @@ -26,10 +27,8 @@ def segment(self):
return str(self._key or "<key>")

def patch(self, key):
"""
Patches path
:type key: str or None
"""
# type: (str) -> Self
"""Patches the path"""
self._key = key
self._parent = self.collection
self.__class__ = EntityPath
Expand Down
15 changes: 15 additions & 0 deletions tests/onedrive/test_excel_ranges.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from office365.onedrive.driveitems.driveItem import DriveItem
from office365.onedrive.workbooks.names.named_item import WorkbookNamedItem
from office365.onedrive.workbooks.ranges.range import WorkbookRange
from tests import create_unique_name
from tests.graph_case import GraphTestCase
from tests.onedrive.test_excel import upload_excel
Expand All @@ -8,6 +9,7 @@
class TestExcelRanges(GraphTestCase):
excel_file = None # type: DriveItem
named_item = None # type: WorkbookNamedItem
range = None # type: WorkbookRange
sheet_name = create_unique_name("Sheet")

@classmethod
Expand All @@ -30,3 +32,16 @@ def test1_name_create(self):
def test2_names_get(self):
result = self.__class__.named_item.get().execute_query()
self.assertIsNotNone(result.resource_path)

def test3_list_range(self):
result = self.__class__.named_item.range().execute_query()
self.assertIsNotNone(result.address)
self.__class__.range = result

# def test4_insert_range(self):
# result = self.__class__.range.insert("Right").execute_query()
# self.assertIsNotNone(result.address)

def test5_clear_range(self):
result = self.__class__.range.clear().execute_query()
self.assertIsNotNone(result.address)
2 changes: 1 addition & 1 deletion tests/onedrive/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def test7_driveitem_delete_permission(self):
perm_to_delete.delete_object().execute_query()

def test8_driveitem_grant_access(self):
file_abs_url = "{0}/Shared Documents/big_buck_bunny.mp4".format(
file_abs_url = "{0}/Shared Documents/Financial Sample.xlsx".format(
test_team_site_url
)
permissions = (
Expand Down

0 comments on commit dd08028

Please sign in to comment.