diff --git a/examples/auth/with_client_secret.py b/examples/auth/with_client_secret.py new file mode 100644 index 00000000..122e1a06 --- /dev/null +++ b/examples/auth/with_client_secret.py @@ -0,0 +1,28 @@ +""" +Acquires a token by using application secret + +The following options are supported: + - utilize built in GraphClient.with_client_secret(tenant, client_id, client_secret, scopes, token_cache) method + - or provide a custom callback function to GraphClient constructor as demonstrated below + +https://learn.microsoft.com/en-us/entra/identity-platform/msal-authentication-flows#client-credentials +""" +import msal + +from office365.graph_client import GraphClient +from tests import test_client_id, test_client_secret, test_tenant + + +def acquire_token(): + authority_url = "https://login.microsoftonline.com/{0}".format(test_tenant) + app = msal.ConfidentialClientApplication( + authority=authority_url, + client_id=test_client_id, + client_credential=test_client_secret, + ) + return app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"]) + + +client = GraphClient(acquire_token) +root_site = client.sites.root.get().execute_query() +print(root_site.web_url) diff --git a/office365/outlook/calendar/events/event.py b/office365/outlook/calendar/events/event.py index b74be471..ffeca943 100644 --- a/office365/outlook/calendar/events/event.py +++ b/office365/outlook/calendar/events/event.py @@ -8,6 +8,7 @@ from office365.entity_collection import EntityCollection from office365.outlook.calendar.attendees.attendee import Attendee from office365.outlook.calendar.dateTimeTimeZone import DateTimeTimeZone +from office365.outlook.calendar.response_status import ResponseStatus from office365.outlook.item import OutlookItem from office365.outlook.mail.attachments.collection import AttachmentCollection from office365.outlook.mail.item_body import ItemBody @@ -195,6 +196,30 @@ def body_preview(self): """The preview of the message associated with the event. It is in text format.""" return self.properties.get("bodyPreview", None) + @property + def reminder_minutes_before_start(self): + # type: () -> Optional[int] + """The number of minutes before the event start time that the reminder alert occurs.""" + return self.properties.get("reminderMinutesBeforeStart", None) + + @property + def response_requested(self): + # type: () -> Optional[bool] + """Default is true, which represents the organizer would like an invitee to send a response to the event.""" + return self.properties.get("responseRequested", None) + + @property + def response_status(self): + # type: () -> Optional[str] + """Indicates the type of response sent in response to an event message.""" + return self.properties.get("responseStatus", ResponseStatus()) + + @property + def series_master_id(self): + # type: () -> Optional[str] + """The ID for the recurring series master item, if this event is part of a recurring series.""" + return self.properties.get("seriesMasterId", None) + @property def subject(self): # type: () -> Optional[str] @@ -212,6 +237,26 @@ def location(self): """The location of the event.""" return self.properties.get("location", Location()) + @property + def transaction_id(self): + # type: () -> Optional[str] + """ + A custom identifier specified by a client app for the server to avoid redundant POST operations in case of + client retries to create the same event. This is useful when low network connectivity causes the client to + time out before receiving a response from the server for the client's prior create-event request. + After you set transactionId when creating an event, you cannot change transactionId in a subsequent update. + This property is only returned in a response payload if an app has set it + """ + return self.properties.get("transactionId", None) + + @property + def type(self): + # type: () -> Optional[str] + """ + The event type. Possible values are: singleInstance, occurrence, exception, seriesMaster + """ + return self.properties.get("type", None) + @property def web_link(self): # type: () -> Optional[str] diff --git a/office365/outlook/calendar/response_status.py b/office365/outlook/calendar/response_status.py new file mode 100644 index 00000000..4b6933d1 --- /dev/null +++ b/office365/outlook/calendar/response_status.py @@ -0,0 +1,11 @@ +from office365.runtime.client_value import ClientValue + + +class ResponseStatus(ClientValue): + """Represents the response status of an attendee or organizer for a meeting request.""" + + def __init__(self, response=None): + """ + :type response: str + """ + self.response = response diff --git a/tests/directory/test_governance.py b/tests/directory/test_governance.py index 01980f1f..b678925f 100644 --- a/tests/directory/test_governance.py +++ b/tests/directory/test_governance.py @@ -1,13 +1,15 @@ from unittest import TestCase from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_client_credentials +from tests import test_client_id, test_client_secret, test_tenant class TestIdentityGovernance(TestCase): @classmethod def setUpClass(cls): - cls.client = GraphClient(acquire_token_by_client_credentials) + cls.client = GraphClient.with_client_secret( + test_tenant, test_client_id, test_client_secret + ) def test1_list_app_consent_requests(self): result = ( diff --git a/tests/directory/test_identity.py b/tests/directory/test_identity.py index c7bc7ae7..4be4518b 100644 --- a/tests/directory/test_identity.py +++ b/tests/directory/test_identity.py @@ -1,13 +1,15 @@ from unittest import TestCase from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_client_credentials +from tests import test_client_id, test_client_secret, test_tenant class TestIdentity(TestCase): @classmethod def setUpClass(cls): - cls.client = GraphClient(acquire_token_by_client_credentials) + cls.client = GraphClient.with_client_secret( + test_tenant, test_client_id, test_client_secret + ) def test1_list_identity_providers(self): result = self.client.identity.identity_providers.get().execute_query() diff --git a/tests/directory/test_invitations.py b/tests/directory/test_invitations.py index 286ae380..96d2dec8 100644 --- a/tests/directory/test_invitations.py +++ b/tests/directory/test_invitations.py @@ -1,13 +1,15 @@ from unittest import TestCase from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_client_credentials +from tests import test_client_id, test_client_secret, test_tenant class TestInvitations(TestCase): @classmethod def setUpClass(cls): - cls.client = GraphClient(acquire_token_by_client_credentials) + cls.client = GraphClient.with_client_secret( + test_tenant, test_client_id, test_client_secret + ) def test1_create_invitation(self): invitation = self.client.invitations.create( diff --git a/tests/directory/test_organization.py b/tests/directory/test_organization.py index 1bd57ddd..b26ffdad 100644 --- a/tests/directory/test_organization.py +++ b/tests/directory/test_organization.py @@ -1,13 +1,15 @@ from unittest import TestCase from office365.graph_client import GraphClient -from tests.graph_case import acquire_token_by_client_credentials +from tests import test_client_id, test_client_secret, test_tenant class TestOrganization(TestCase): @classmethod def setUpClass(cls): - cls.client = GraphClient(acquire_token_by_client_credentials) + cls.client = GraphClient.with_client_secret( + test_tenant, test_client_id, test_client_secret + ) def test1_list(self): org = self.client.organization.get().execute_query() diff --git a/tests/graph_case.py b/tests/graph_case.py index 0fc27838..5074c891 100644 --- a/tests/graph_case.py +++ b/tests/graph_case.py @@ -30,19 +30,6 @@ def acquire_token_by_username_password(): return result -def acquire_token_by_client_credentials(): - settings = load_settings() - authority_url = "https://login.microsoftonline.com/{0}".format( - settings.get("default", "tenant") - ) - app = msal.ConfidentialClientApplication( - authority=authority_url, - client_id=settings.get("client_credentials", "client_id"), - client_credential=settings.get("client_credentials", "client_secret"), - ) - return app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"]) - - class GraphTestCase(TestCase): """Microsoft Graph specific test case base class""" diff --git a/tests/onedrive/test_term_store.py b/tests/onedrive/test_term_store.py index 4f3bec58..73a3cc56 100644 --- a/tests/onedrive/test_term_store.py +++ b/tests/onedrive/test_term_store.py @@ -5,8 +5,8 @@ from office365.onedrive.termstore.sets.set import Set from office365.onedrive.termstore.store import Store from office365.onedrive.termstore.terms.term import Term -from tests import test_root_site_url -from tests.graph_case import GraphTestCase, acquire_token_by_client_credentials +from tests import test_client_id, test_client_secret, test_root_site_url, test_tenant +from tests.graph_case import GraphTestCase class TestTermStore(GraphTestCase): @@ -18,7 +18,9 @@ class TestTermStore(GraphTestCase): @classmethod def setUpClass(cls): super(TestTermStore, cls).setUpClass() - client = GraphClient(acquire_token_by_client_credentials) + client = GraphClient.with_client_secret( + test_tenant, test_client_id, test_client_secret + ) cls.target_store = client.sites.get_by_url(test_root_site_url).term_store @classmethod