Giter Club home page Giter Club logo

msgraph-sdk-python's Introduction

PyPI version Downloads Supported Versions Contributors

Microsoft Graph SDK for Python

Get started with the Microsoft Graph SDK for Python by integrating the Microsoft Graph API into your Python application.

Note:

  • This SDK allows you to build applications using the v1.0 of Microsoft Graph. If you want to try the latest Microsoft Graph APIs, try the beta SDK.

1. Installation

pip install msgraph-sdk

Note:

  • The Microsoft Graph SDK for Python is a fairly large package. It may take a few minutes for the initial installation to complete.
  • Enable long paths in your environment if you receive a Could not install packages due to an OSError. For details, see Enable Long Paths in Windows 10, Version 1607, and Later.

2. Getting started with Microsoft Graph

2.1 Register your application

Register your application by following the steps at Register your app with the Microsoft Identity Platform.

2.2 Select and create an authentication provider

To start writing code and making requests to the Microsoft Graph service, you need to set up an authentication provider. This object will authenticate your requests to Microsoft Graph. For authentication, the Microsoft Graph Python SDK supports both sync and async credential classes from Azure Identity. Which library to choose depends on the type of application you are building.

Note: For authentication we support both sync and async credential classes from azure.identity. Please see the azure identity docs for more information.

The easiest way to filter this decision is by looking at the permissions set you'd use. Microsoft Graph supports 2 different types of permissions: delegated and application permissions:

  • Application permissions are used when you don’t need a user to login to your app, but the app will perform tasks on its own and run in the background.
  • Delegated permissions, also called scopes, are used when your app requires a user to login and interact with data related to this user in a session.

The following table lists common libraries by permissions set.

MSAL library Permissions set Common use case
ClientSecretCredential Application permissions Daemon apps or applications running in the background without a signed-in user.
DeviceCodeCredential Delegated permissions Enviroments where authentication is triggered in one machine and completed in another e.g in a cloud server.
InteractiveBrowserCredentials Delegated permissions Environments where a browser is available and the user wants to key in their username/password.
AuthorizationCodeCredentials Delegated permissions Usually for custom customer applications where the frontend calls the backend and waits for the authorization code at a particular url.

You can also use EnvironmentCredential, DefaultAzureCredential, OnBehalfOfCredential, or any other Azure Identity library.

Once you've picked an authentication library, we can initiate the authentication provider in your app. The following example uses ClientSecretCredential with application permissions.

import asyncio

from azure.identity.aio import ClientSecretCredential

credential = ClientSecretCredential("tenantID",
                                    "clientID",
                                    "clientSecret")
scopes = ['https://graph.microsoft.com/.default']

The following example uses DeviceCodeCredentials with delegated permissions.

import asyncio

from azure.identity import DeviceCodeCredential

credential = DeviceCodeCredential("client_id",
                                  "tenant_id")
graph_scopes = ['User.Read', 'Calendars.ReadWrite.Shared']

2.3 Initialize a GraphServiceClient object

You must create GraphServiceClient object to make requests against the service. To create a new instance of this class, you need to provide credentials and scopes, which can authenticate requests to Microsoft Graph.

# Example using async credentials and application access.
from azure.identity.aio import ClientSecretCredential
from msgraph import GraphServiceClient

credentials = ClientSecretCredential(
    'TENANT_ID',
    'CLIENT_ID',
    'CLIENT_SECRET',
)
scopes = ['https://graph.microsoft.com/.default']
client = GraphServiceClient(credentials=credentials, scopes=scopes)

The above example uses default scopes for app-only access. If using delegated access you can provide custom scopes:

# Example using sync credentials and delegated access.
from azure.identity import DeviceCodeCredential
from msgraph import GraphServiceClient

credentials = DeviceCodeCredential(
    'CLIENT_ID',
    'TENANT_ID',
)
scopes = ['User.Read', 'Mail.Read']
client = GraphServiceClient(credentials=credentials, scopes=scopes)

Note: Refer to the following documentation page if you need to configure an HTTP proxy.

3. Make requests against the service

After you have a GraphServiceClient that is authenticated, you can begin making calls against the service. The requests against the service look like our REST API.

Note: This SDK offers an asynchronous API by default. Async is a concurrency model that is far more efficient than multi-threading, and can provide significant performance benefits and enable the use of long-lived network connections such as WebSockets. We support popular python async envronments such as asyncio, anyio or trio.

The following is a complete example that shows how to fetch a user from Microsoft Graph.

import asyncio
from azure.identity.aio import ClientSecretCredential
from msgraph import GraphServiceClient

credential = ClientSecretCredential(
    'tenant_id',
    'client_id',
    'client_secret'
)
scopes = ['https://graph.microsoft.com/.default']
client = GraphServiceClient(credentials=credential, scopes=scopes)

# GET /users/{id | userPrincipalName}
async def get_user():
    user = await client.users.by_user_id('userPrincipalName').get()
    if user:
        print(user.display_name)
asyncio.run(get_user())

Note that to calling me requires a signed-in user and therefore delegated permissions. See Authenticating Users for more:

import asyncio
from azure.identity import InteractiveBrowserCredential
from msgraph import GraphServiceClient

credential = InteractiveBrowserCredential()
scopes=['User.Read']
client = GraphServiceClient(credentials=credential, scopes=scopes,)

# GET /me
async def me():
    me = await client.me.get()
    if me:
        print(me.display_name)
asyncio.run(me())

3.1 Error Handling

Failed requests raise APIError exceptions. You can handle these exceptions using try catch statements.

from kiota_abstractions.api_error import APIError
async def get_user():
    try:
        user = await client.users.by_user_id('userID').get()
        print(user.user_principal_name, user.display_name, user.id)
    except APIError as e:
        print(f'Error: {e.error.message}')
asyncio.run(get_user())

3.2 Pagination

By default a maximum of 100 rows are returned but in the response if odata_next_link is present, it can be used to fetch the next batch of max 100 rows. Here's an example to fetch the initial rows of members in a group, then iterate over the pages of rows using the odata_next_link

        # get group members
        members = await client.groups.by_group_id(id).members.get()
        if members:
            print(f"########## Members:")
            for i in range(len(members.value)):
                print(f"display_name: {members.value[i].display_name}, mail: {members.value[i].mail}, id: {members.value[i].id}")

        # iterate over result batches > 100 rows
        while members is not None and members.odata_next_link is not None:
            members = await client.groups.by_group_id(id).members.with_url(members.odata_next_link).get()
            if members:
                print(f"########## Members:")
                for i in range(len(members.value)):
                    print(f"display_name: {members.value[i].display_name}, mail: {members.value[i].mail}, id: {members.value[i].id}")

Documentation and resources

Upgrading

For detailed information on breaking changes, bug fixes and new functionality introduced during major upgrades, check out our Upgrade Guide

Issues

View or log issues on the Issues tab in the repo.

Contribute

Please read our Contributing guidelines carefully for advice on how to contribute to this repo.

Copyright and license

Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT license.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

Third Party Notices

Third-party notices

msgraph-sdk-python's People

Contributors

abetomo avatar andrueastman avatar baywet avatar cnotin avatar dabla avatar dependabot[bot] avatar drakezulsminimalism avatar earthquakesan avatar github-actions[bot] avatar isvargasmsft avatar michaelmainer avatar michaelyochpaz avatar ndiritu avatar samwelkanda avatar silaskenneth avatar stbrie avatar sujaywork avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

msgraph-sdk-python's Issues

Profile Photo content exception

Receiving the following exception when attempting to retrieve the profile photo using the content query builder:

File "...\Python\Python311\Lib\site-packages\kiota_abstractions\serialization\parse_node_factory_registry.py", line 50, in get_root_parse_node
raise Exception(
Exception: Content type image/jpeg does not have a factory registered to be parsed

from:
pic = await (client.users_by_id(id).photo.content.get())

as was mentioned in issue#92 using the $value as an optional query parameter is not an option.

Are there any workarounds?

Send message in a Teams channel using msgraph-sdk-python

Hello,

I have been trying to find a way to send a message in a Teams channel using msgraph-sdk-python but I do not seem to find it. Is there a method for this matter? And if there is one, where can I find the post function ?

I noticed that I can do this with Graph Explorer using the {team ID} and the {channel ID} in the Beta version of Microsoft Teams.

Thank you for your time

Incorrectly configured request headers in me_request_builder.py

Version:

msgraph-sdk 1.0.0a9

When attempting to run the Microsoft Graph SDK for python, an error is thrown during execution.

Source Tutorial: https://learn.microsoft.com/en-us/graph/tutorials/python?tabs=aad&tutorial-step=4

In this step, running user = await self.user_client.me.get(request_configuration=request_config) will fail. wrapping this in an try/except clause provides the following error.

Error: The MIME type 'a, p, p, l, i, c, a, t, i, o, n, /, j, s, o, n' requires a '/' character between type and subtype, such as 'text/plain'.

MS ToDo Compatibility

Hello,

I am attempting to connect to MS ToDo by adapting the provided tutorial here: https://learn.microsoft.com/en-us/graph/tutorials/python?tabs=aad

A Problem I have been running into is the TodoRequestBuilderDeleteRequestConfiguration object seems to require query_parameters attribute even though it does not seem to allow it when first instantiated. The specific function I have is:

    async def get_list_of_tasks(self):

        request_config = TodoRequestBuilder.TodoRequestBuilderGetRequestConfiguration()
        
        messages = await self.user_client.me.todo.lists_by_id('all').get(
                request_configuration=request_config)
        return messages

This is new territory for me so it's possible I am just misinterpreting how this is supposed to work.

Problem with API responses with string 'none'

Hi,

it seems there is a problem with api responses with the string "none". Here is an example, but I think this might happen more often.

Problem

When fetching sign-ins, an exception from kiota_serialization_json is raised.

Minimal example for reproduction:


import asyncio

from azure.identity import DefaultAzureCredential
from msgraph import GraphRequestAdapter
from msgraph import GraphServiceClient
from kiota_authentication_azure.azure_identity_authentication_provider import AzureIdentityAuthenticationProvider

from msgraph.generated.audit_logs.sign_ins.sign_ins_request_builder import SignInsRequestBuilder


# Acquire a credential object
credential = DefaultAzureCredential()
auth_provider = AzureIdentityAuthenticationProvider(credential)
adapter = GraphRequestAdapter(auth_provider)
client = GraphServiceClient(adapter)

async def get_failed_sign_ins():
  query_params = SignInsRequestBuilder.SignInsRequestBuilderGetQueryParameters(
    top=10, filter='createdDateTime gt 2023-01-30 and status/errorCode ne 0'
  )
  request_config = SignInsRequestBuilder.SignInsRequestBuilderGetRequestConfiguration(
    query_parameters=query_params
  )

  failed_sign_ins = await (client.audit_logs.sign_ins.get(request_configuration=request_config))

asyncio.run(get_failed_sign_ins())

The exception raised is Invalid key: None for enum <enum 'RiskState'>. . This seems to happen, because the API Response contains the string 'none' for this field, which is turned into 'None' by preprocessing in kiota_serialization_json. The following is a the corresponding line in the dataclass. It seems to expect 'None_'.

Best

Tobias

Get list items of Sharepoint list

I try to get the items of a Sharepoint list but don't get it working. Here is a code snippet:

    async def get_sp_site_list_items(self, list_id):

        query_params = ItemsRequestBuilder.ItemsRequestBuilderGetQueryParameters(
            expand=['fields']
            #select=['Title'] # does not work -> Could not find a property named 'Title' on type 'microsoft.graph.listItem'
            #filter='siteCollection/root',
            #orderby=['createdDateTime']
        )
        request_config = ItemsRequestBuilder.ItemsRequestBuilderGetRequestConfiguration(
            query_parameters = query_params
        )

        try:
            resp = await self.app_client.sites_by_id(self.site_id).lists_by_id(list_id).items.get(request_configuration=request_config)
            print("\nThe list " + list_id + " has the following items:\n")
            for item in resp.value:
                print(item.id)
                print(item.fields)
        except Exception as e:
            print(f'Error: {e.error.message}')

        return

Using this snippet I get a FieldValueSet alright, but I can not get the contents. I found out the columns with columns.get(), but neither selecting in the request_configuration nor trying items.fields.columname leads to anything. So I am wondering: Am I doing something completely wrong with the code above or is something else wrong here?

Telemetry handler experience

2 things to address:

  • The telemetry handler would only set the client request Id if not already present, allowing people to set it manually if they need some kind of end-to-end tracing.
  • The telemetry handler should start a span and attach the client request id as a tag.

lazy import not found - django watch file's reload

When running django locally using the runserver command and the following version installed:

msgraph-sdk==1.0.0a9
Django==4.1

I receive the following error:

System check identified no issues (0 silenced).
February 11, 2023 - 11:18:08
Django version 4.1
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Traceback (most recent call last):
  File "/home/XXXXXX/XXXXXXX/manage.py", line 22, in <module>
    main()
  File "/home/XXXXXX/XXXXXXX/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/core/management/base.py", line 402, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/core/management/commands/runserver.py", line 74, in execute
    super().execute(*args, **options)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/core/management/commands/runserver.py", line 111, in handle
    self.run(**options)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/core/management/commands/runserver.py", line 118, in run
    autoreload.run_with_reloader(self.inner_run, **options)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 680, in run_with_reloader
    start_django(reloader, main_func, *args, **kwargs)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 661, in start_django
    reloader.run(django_main_thread)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 344, in run
    self.run_loop()
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 350, in run_loop
    next(ticker)
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 390, in tick
    for filepath, mtime in self.snapshot_files():
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 411, in snapshot_files
    for file in self.watched_files():
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 304, in watched_files
    yield from iter_all_python_module_files()
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 115, in iter_all_python_module_files
    modules = tuple(
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/django/utils/autoreload.py", line 118, in <genexpr>
    if not isinstance(m, weakref.ProxyTypes)
  File "/usr/lib/python3.10/importlib/util.py", line 247, in __getattribute__
    self.__spec__.loader.exec_module(self)
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/msgraph/generated/identity_governance/entitlement_management/assignments/item/access_package_assignment_item_request_builder.py", line 16, in <module>
    target_request_builder = lazy_import('msgraph.generated.identity_governance.entitlement_management.assignments.item.target.target_request_builder')
  File "/home/XXXXXX/.virtualenvs/XXXXXXX/lib/python3.10/site-packages/kiota_abstractions/utils.py", line 24, in lazy_import
    spec = importlib.util.find_spec(name)
  File "/usr/lib/python3.10/importlib/util.py", line 94, in find_spec
    parent = __import__(parent_name, fromlist=['__path__'])
ModuleNotFoundError: No module named 'msgraph.generated.identity_governance.entitlement_management.assignments.item.target'

It appears that the lazy import in:

target_request_builder = lazy_import('msgraph.generated.identity_governance.entitlement_management.assignments.item.target.target_request_builder')

is pointing at a non-existent kiota generated instance.

Let me know if you need anymore info.

Temporary solution: removing/commenting this import, then the application is able to run fine, or adding the --noreload cli argument also prevent the django watch files from trying to resolve this path.

error in example from readme: The provided value for scope `User.Read` is not valid. Client credential flows must have a scope value with `/.default`

Hello,

Running the example given in the readme throw following error:

azure.core.exceptions.ClientAuthenticationError: Authentication failed: AADSTS1002012: The provided value for scope User.Read is not valid. Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).

I'm using from azure.identity import ClientSecretCredential to generate the crendential object, and use the scope given by the example scopes = ['User.Read'], the error message is clear for me, Azure AD only accepts /.default scope for ClientSecretCredential auth flow. I would like to know how to specify specific scope instead of /.default

How to get children of the root of a Drive?

Using 1.0.0a9 I was able to use this line to get the children of the root of a drive:

children = await (self.app_client.drives_by_id(drive.id).root.children.get())

but after upgrading to 1.0.0a11 I get "'RootRequestBuilder' object has no attribute 'children'"

directory_audits throws Invalid key: ClientError for enum .

I am using version 1.0.0a12 of the msgraph sdk with Python 3.10. When I try to get the directory audits on my Azure AD B2C tenant an exception gets thrown during serialization:

from azure.identity import ClientSecretCredential
from kiota_authentication_azure.azure_identity_authentication_provider import AzureIdentityAuthenticationProvider
from msgraph import GraphRequestAdapter, GraphServiceClient

TENANT_ID = ''
CLIENT_ID = ''
CLIENT_SECRET = ''
SCOPES = ['https://graph.microsoft.com/.default']

credential = ClientSecretCredential(
    tenant_id=TENANT_ID,
    client_id=CLIENT_ID,
    client_secret=CLIENT_SECRET
)
auth_provider = AzureIdentityAuthenticationProvider(credential, scopes=SCOPES)
request_adapter = GraphRequestAdapter(auth_provider)
graph_client = GraphServiceClient(request_adapter)

audit = await graph_client.audit_logs.directory_audits.get()

throws

KeyError                                  Traceback (most recent call last)
File /local_disk0/.ephemeral_nfs/envs/pythonEnv-27662e6a-52e6-45ef-a9cd-132d7d2500d4/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py:213, in JsonParseNode.get_enum_value(self, enum_class)
    212 try:
--> 213     return enum_class[camel_case_key]  # type: ignore
    214 except KeyError:

File /usr/lib/python3.10/enum.py:440, in EnumMeta.__getitem__(cls, name)
    439 def __getitem__(cls, name):
--> 440     return cls._member_map_[name]

It takes 3 minutes to import modules from msgraph

Hi,
It looks like importing modules from msgraph takes more than 3 minutes.

import time
start = time.time()
from msgraph import GraphRequestAdapter, GraphServiceClient
print(time.time() - start)
217.1570749282837

Could you give me some advice?
Thanks.

OneDrive path based addressing

DriveItems support file paths as part of the URL:

e.g.

/me/drive/root:/{item-path}:/createUploadSession

Could I confirm that the msgraph-sdk has no support for this at the moment? I've had a bit of a poke around, and it seems like there's no urlTemplate like this and no mechanism to set something like item-path.

MS ToDo compatibility

Hi @samwelkanda,

I am opnening this back up as I think the compatibility is still in question. I might be wayyy wrong so no worries if this is better off closed, but here is the issue I am having after solving the basic setup issues. Thanks for any advice you can give on solving this!
Created from #106 as it looks like I cannot reopen that one (it's messy anyway).

Problem description

With the current library, the client.me.todo methods can only access "list" information. No matter what the actual MS ToDo app shows in those lists, the "tasks" attribute is always "None". Here is some example output from the script below. Note, the "tasks: None" section in the output from each list.

Name:  Tasks  tasks:  None  id:  AAMk...
Name:  sharing test  tasks:  None  id:  AAMk...
Name:  Tags  tasks:  None  id:  AAMk...
Name:  test list  tasks:  None  id:  AAMk...
Name:  testapi  tasks:  None  id:  AAMk...
Name:  Flagged Emails  tasks:  None  id:  AAMk...

Solution options:

  • One solution for this would be a clean way to get and send raw http GET/POST etc. requests using this library to authenticate.
  • The other would be a possible fix to the code below if I am missing something or if this library needs additions

code to recreate the problem

I have created a stand alone test for the problem I am seeing. It is just a cfg file for my api info and this python script:

import asyncio
from azure.identity import DeviceCodeCredential
from kiota_authentication_azure.azure_identity_authentication_provider import AzureIdentityAuthenticationProvider
from msgraph import GraphRequestAdapter, GraphServiceClient
from msgraph.generated.me.todo.lists.lists_request_builder import ListsRequestBuilder
from kiota_abstractions.native_response_handler import NativeResponseHandler
import httpx
import configparser

# Load settings
config = configparser.ConfigParser()
config.read(['config.cfg', 'config.dev.cfg'])
azure_settings = config['azure']
settings = azure_settings
client_id = settings['clientId']
tenant_id = settings['tenantId']
graph_scopes = settings['graphUserScopes'].split(' ')

# Authentication
device_code_credential = DeviceCodeCredential(client_id, tenant_id = tenant_id)
auth_provider = AzureIdentityAuthenticationProvider(
    device_code_credential,
    scopes=graph_scopes)

# instances
request_adapter = GraphRequestAdapter(auth_provider)
client = GraphServiceClient(request_adapter)


async def main():
    t_lists = await get_lists()

    # show all the lists:
    [print('Name: ', a.display_name, ' tasks: ', a.tasks, ' id: ', a.id) for a in t_lists.value]
    print('/n')

    # attempt to get a task from first list via lists_by_id method:
    first_list_tasks = await get_lists_by_id(t_lists.value[0].id)
    print('tasks:', first_list_tasks.tasks)


async def get_lists():
    t_lists = await client.me.todo.lists.get()
    return t_lists


async def get_lists_by_id(list_id):
    t_lists = await client.me.todo.lists_by_id(list_id).get()
    return t_lists


asyncio.run(main())

The config format as a file named : "config.cfg" I have removed my client and tenant ids.

[azure]
clientId = REMOVED
tenantId = REMOVED
graphUserScopes = User.Read Mail.Read Mail.Send Tasks.ReadWrite

Error encountered when running sample code from readme

when executing sample code from the readme, encountered the below error.
can someone help me out here, is it something that I'm missing, or its the package?

Traceback (most recent call last):
  File "/home/devuser/es-bitbucket/od_dev/poc/auth/auth.py", line 25, in <module>
    asyncio.run(me())
  File "/usr/local/custom-openssl/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/custom-openssl/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
    return future.result()
  File "/home/devuser/es-bitbucket/od_dev/poc/auth/auth.py", line 21, in me
    me = await client.me.get()
  File "/home/devuser/es-bitbucket/od_dev/poc/lib/python3.10/site-packages/msgraph/generated/me/me_request_builder.py", line 794, in get
    return await self.request_adapter.send_async(request_info, user.User, response_handler, error_mapping)
TypeError: HttpxRequestAdapter.send_async() takes 4 positional arguments but 5 were given

@samwelkanda

Getting DriveItems under a folder

I am able to get the DriveItems in the root of a user's OneDrive using the following:

children = await self.app_client.drives_by_id(drive.id).root().children().get()

And then I loop through the returned DriveItems and want to then get the items inside one of the folders but can't seem to get it to execute a GET to /drives/{drive-id}/items/{item-id}/children (which is in the url_template in the children_request_builder). I've tried the following with no luck:

children = await self.app_client.drives_by_id(drive.id).items_by_id(driveItem.id).children().get()

Would love some sample code if anyone has it along with which generated imports to pull in.

Calling applications.get() throws NameError: name 'u_u_i_d' is not defined

Calling apps = await (msgraph_client.applications.get())

Throws:

j=279695 Traceback (most recent call last):
j=279695     apps = await (msgraph_client.applications.get())
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/msgraph/generated/applications/applications_request_builder.py", line 63, in get
j=279695     return await self.request_adapter.send_async(request_info, application_collection_response.ApplicationCollectionResponse, error_mapping)
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_http/httpx_request_adapter.py", line 122, in send_async
j=279695     result = root_node.get_object_value(model_type)
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 226, in get_object_value
j=279695     self._assign_field_values(result)
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 285, in _assign_field_values
j=279695     deserializer(JsonParseNode(val))
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/msgraph/generated/models/application_collection_response.py", line 39, in <lambda>
j=279695     "value": lambda n : setattr(self, 'value', n.get_collection_of_object_values(application.Application)),
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 178, in get_collection_of_object_values
j=279695     return list(
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 180, in <lambda>
j=279695     lambda x: JsonParseNode(x).get_object_value(factory),  # type: ignore
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 226, in get_object_value
j=279695     self._assign_field_values(result)
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 285, in _assign_field_values
j=279695     deserializer(JsonParseNode(val))
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/msgraph/generated/models/application.py", line 382, in <lambda>
j=279695     "api": lambda n : setattr(self, 'api', n.get_object_value(api_application.ApiApplication)),
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 226, in get_object_value
j=279695     self._assign_field_values(result)
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/kiota_serialization_json/json_parse_node.py", line 285, in _assign_field_values
j=279695     deserializer(JsonParseNode(val))
j=279695   File "/Users/dymartin/.pyenv/versions/divvy-dev/lib/python3.8/site-packages/msgraph/generated/models/api_application.py", line 85, in <lambda>
j=279695     "knownClientApplications": lambda n : setattr(self, 'known_client_applications', n.get_collection_of_primitive_values(u_u_i_d)),
j=279695 NameError: name 'u_u_i_d' is not defined

Works for other types like groups and serviceprinicpals. I'm following the pagination example in the samples file.

I'm finding this new version of the package much more confusing to use than the previous. It also doesn't seem to include all the available endpoints on MSGraph including any beta api's like userRegistrationDetails resource type.

filterByCurrentUser never works

I'm trying to retrieve the list of access packages a user is allowed to request. The correct request URL is /identityGovernance/entitlementManagement/accessPackages/filterByCurrentUser(on='allowedRequestor') which I'm calling through the SDK with access_packages = await client.identity_governance.entitlement_management.access_packages.filter_by_current_user_with_on('allowedRequestor').get() but the actual URL generated and submitted is /identityGovernance/entitlementManagement/accessPackages/filterByCurrentUser(on='') which unsurprisingly returns an error.

The file msgraph/generated/identity_governance/entitlement_management/access_packages/filter_by_current_user_with_on/filter_by_current_user_with_on_request_builder.py contains the line

url_tpl_params[""] = on

and as a result the filterByCurrentUser(on='{on}') part of the URL template is not correctly filled in when the request is prepared. Changing this to

url_tpl_params["on"] = on

allows the request to succeed and return the expected list of access packages.

Grepping through the library it looks like every instance of filter_by_current_user_with_on_request_builder.py has this error.

msgraph.generated.models.o_data_errors.o_data_error.ODataError when self.user_client.me.todo.lists.get()

I am getting error msgraph.generated.models.o_data_errors.o_data_error.ODataError.

config.cfg

[azure]
clientId = ***
tenantId = ***
graphUserScopes = Tasks.Read Directory.Read.All User.ReadBasic.All User.Read
clientSecret = ***

graphy.py

class Graph:
    settings: SectionProxy
    device_code_credential: DeviceCodeCredential
    adapter: GraphRequestAdapter
    user_client: GraphServiceClient

    def __init__(self, config: SectionProxy):
        self.settings = config
        client_id = self.settings["clientId"]
        tenant_id = self.settings["tenantId"]
        graph_scopes = self.settings["graphUserScopes"].split(" ")

        self.device_code_credential = DeviceCodeCredential(
            client_id, tenant_id=tenant_id
        )
        auth_provider = AzureIdentityAuthenticationProvider(
            self.device_code_credential, scopes=graph_scopes
        )
        self.adapter = GraphRequestAdapter(auth_provider)
        self.user_client = GraphServiceClient(self.adapter)

    async def get_user_token(self):
        graph_scopes = self.settings["graphUserScopes"]
        access_token = self.device_code_credential.get_token(graph_scopes)
        return access_token.token

    async def get(self):
        user = await self.user_client.me.todo.lists.get()
        print(user)

main.py

import asyncio
import configparser
from graph import Graph

async def main():
    print("Python Graph Tutorial\n")

    # Load settings
    config = configparser.ConfigParser()
    config.read(["config.cfg", "config.dev.cfg"])
    azure_settings = config["azure"]

    graph: Graph = Graph(azure_settings)
    await graph.get()

asyncio.run(main())

Get message content error

The error

Following sample code generates exception:

...
response = await GraphServiceClient(GraphRequestAdapter(auth_provider)).me.messages_by_id(id).content.get()
...
...
File "/home/vscode/getmail/getmail.py", line 101, in getmail
    response = await GraphServiceClient(GraphRequestAdapter(auth_provider)).me.messages_by_id(id).content.get()
  File "/home/vscode/.local/lib/python3.9/site-packages/msgraph/generated/me/messages/item/value/content_request_builder.py", line 53, in get
    return await self.request_adapter.send_primitive_async(request_info, "bytes", error_mapping)
  File "/home/vscode/.local/lib/python3.9/site-packages/kiota_http/httpx_request_adapter.py", line 227, in send_primitive_async
    raise Exception("Found unexpected type to deserialize")
Exception: Found unexpected type to deserialize

Package Version


aiohttp 3.8.4
aiosignal 1.3.1
anyio 3.6.2
async-timeout 4.0.2
attrs 22.2.0
azure-core 1.26.3
azure-identity 1.12.0
cachetools 5.3.0
certifi 2022.12.7
cffi 1.15.1
charset-normalizer 3.0.1
cryptography 39.0.1
frozenlist 1.3.3
google-api-core 2.11.0
google-api-python-client 2.74.0
google-auth 2.16.0
google-auth-httplib2 0.1.0
google-auth-oauthlib 0.8.0
googleapis-common-protos 1.58.0
h11 0.14.0
h2 4.1.0
hpack 4.0.0
httpcore 0.16.3
httplib2 0.21.0
httpx 0.23.3
hyperframe 6.0.1
idna 3.4
microsoft-kiota-abstractions 0.4.0
microsoft-kiota-authentication-azure 0.2.0
microsoft-kiota-http 0.4.0
microsoft-kiota-serialization-json 0.2.1
microsoft-kiota-serialization-text 0.2.0
msal 1.21.0
msal-extensions 1.0.0
msgraph-core 1.0.0a4
msgraph-sdk 1.0.0a9
multidict 6.0.4
oauthlib 3.2.2
pip 22.3.1
portalocker 2.7.0
protobuf 4.21.12
pyasn1 0.4.8
pyasn1-modules 0.2.8
pycparser 2.21
PyJWT 2.6.0
pyparsing 3.0.9
python-dateutil 2.8.2
requests 2.28.2
requests-oauthlib 1.3.1
rfc3986 1.5.0
rsa 4.9
setuptools 67.0.0
six 1.16.0
sniffio 1.3.0
typing_extensions 4.4.0
uritemplate 4.1.1
urllib3 1.26.14
wheel 0.38.4
yarl 1.8.2

The workaround

Use native HTTPX response and parse it:

request_configuration = MessagesRequestBuilder.MessagesRequestBuilderGetRequestConfiguration(
    options=[ResponseHandlerOption(NativeResponseHandler())]
)
response = await GraphServiceClient(GraphRequestAdapter(auth_provider)).me.messages_by_id(id).content.get(request_configuration=request_configuration)

Get attachment content is not implemented

Hi,

The Graph API documentation describe how to retrieve attachment content from the API:

https://learn.microsoft.com/en-us/graph/api/attachment-get?view=graph-rest-1.0&tabs=http

GET /me/messages/{id}/attachments/{id}
GET /users/{id | userPrincipalName}/messages/{id}/attachments/{id}

GET /me/messages/{id}/attachments/{id}/$value
GET /users/{id | userPrincipalName}/messages/{id}/attachments/{id}/$value

One example: https://learn.microsoft.com/en-us/graph/api/attachment-get?view=graph-rest-1.0&tabs=http#example-8-get-the-mime-raw-contents-of-an-event-attachment-on-a-message

GET https://graph.microsoft.com/v1.0/me/messages/AAMkADVIOAAA=/attachments/AAMkADVIOAAABEgAQACvkutl6c4FMifPyS6NvXsM=/$value

The issue is $value seems not to be implemented.

class AttachmentItemRequestBuilderGetQueryParameters
        """
        The fileAttachment and itemAttachment attachments for the message.
        """
        # Expand related entities
        expand: Optional[List[str]] = None

        # Select properties to be returned
        select: Optional[List[str]] = None

The class AttachmentItemRequestBuilderGetQueryParameters does not contain value : Optional[bool] = None

So it is not possible to use $value like described in https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http#other-odata-url-capabilities

$value	Retrieves or updates the binary value of an item.	GET /me/photo/$value

Batch Requests

Great work on the package!!

Does the package support batching the get calls?

Skip generation for the /me endpoints

/me and /users/{id} are effectively identical from a metadata perspective. This presents an opportunity to reduce build time further (~17% from our early measurements) while minimally impacting the experience for users.

TODO:

  • in graph core, add a UrlReplaceOption request option. It needs to implement the interface and provide accessors for Enabled (default true) as well as "ReplacementPairs" (map[string]string), default [/users/me-token-to-replace, /me]
  • in graph core add a UrlReplaceHandler that implements the middleware handler, replaces the tokens in the URLs, and traces its activity with the telemetry.
  • in graph core, add this new handler as part of the defaults
  • in code generator, update the go weekly generation steps to exclude /me (exclude paths, /me and /me/**, or something similar)
  • trigger a code generation from beta
  • in that newly generated PR, update the dependency to core
  • in that newly generated PR, cherry pick the first commit from #333
  • update the snippets generation to map inline types for /me to /users

Incorrect url for items_by_id

Using msgraph-sdk-1.0.0a11

When using drives_by_id and then using items_by_id, the URL template is invalid.

query = self.user_client.drives_by_id(id=drive.id).items_by_id(id=item.id)
print(query.url_template)

The URL template for the request is:

{+baseurl}/drives/{drive%2Did}/special/{driveItem%2Did}{?%24select,%24expand}

The path should be /drive/{drive-id}/items/{item-id}. /special/ is for special folders and should not be used by items_by_id

Trying to execute the code provides

Error: The special folder identifier isn't valid

This is caused by the line here:

self.url_template: str = "{+baseurl}/drives/{drive%2Did}/special/{driveItem%2Did}{?%24select,%24expand}"

is this the recommended way to interact with azure AD groups/users using Python at the moment?

I was previously trying to use the azure-graphrbac library, but it seems like that's been deprecated and I can no longer assign the right kind of admin permissions to my service client to use it. This library however seems to be in preview and using the examples here & what little documentation I can find it's been hard to actually accomplish anything.

Is there something I'm missing? Or should I just try a different language/approach?

Thanks!

NameError: name 'Guid' is not defined

Hi,

I'm trying to perform service_principals.get() and the following exception is thrown:

Traceback (most recent call last):
  File "./tests/test_components/test_guid.py", line 36, in <module>
    asyncio.run(run())
  File "C:\Program Files\Python37\lib\asyncio\runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "C:\Program Files\Python37\lib\asyncio\base_events.py", line 587, in run_until_complete
    return future.result()
  File "./tests/test_components/test_guid.py", line 33, in run
    res = await graph.get_service_principals()
  File "./tests/test_components/test_guid.py", line 27, in get_service_principals
    res = await self.user_client.service_principals.get()
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\msgraph\generated\service_principals\service_principals_request_builder.py", line 95, in get
    return await self.request_adapter.send_async(request_info, service_principal_collection_response.ServicePrincipalCollectionResponse, error_mapping)
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\kiota_http\httpx_request_adapter.py", line 122, in send_async
    result = root_node.get_object_value(model_type)
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\kiota_serialization_json\json_parse_node.py", line 226, in get_object_value
    self._assign_field_values(result)
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\kiota_serialization_json\json_parse_node.py", line 287, in _assign_field_values
    deserializer(JsonParseNode(val))
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\msgraph\generated\models\service_principal_collection_response.py", line 36, in <lambda>
    "value": lambda n : setattr(self, 'value', n.get_collection_of_object_values(service_principal.ServicePrincipal)),
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\kiota_serialization_json\json_parse_node.py", line 181, in get_collection_of_object_values
    self._json_node
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\kiota_serialization_json\json_parse_node.py", line 180, in <lambda>
    lambda x: JsonParseNode(x).get_object_value(factory),  # type: ignore
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\kiota_serialization_json\json_parse_node.py", line 226, in get_object_value
    self._assign_field_values(result)
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\kiota_serialization_json\json_parse_node.py", line 287, in _assign_field_values
    deserializer(JsonParseNode(val))
  File "C:\Users\user\AppData\Roaming\Python\Python37\site-packages\msgraph\generated\models\service_principal.py", line 495, in <lambda>
    "app_owner_organization_id": lambda n : setattr(self, 'app_owner_organization_id', n.get_object_value(Guid)),
NameError: name 'Guid' is not defined

This is the code for reproduction:

import asyncio
from azure.identity import UsernamePasswordCredential
from kiota_authentication_azure.azure_identity_authentication_provider import (AzureIdentityAuthenticationProvider)
from msgraph import GraphRequestAdapter, GraphServiceClient


class Graph:
    adapter: GraphRequestAdapter
    user_client: GraphServiceClient

    def __init__(self):
        default_client_id = 'xxx'
        admin_username = 'xxx'
        admin_password = 'xxx'
        tenant_id = 'xxx'

        credential = UsernamePasswordCredential(client_id=default_client_id,
                                                username=admin_username,
                                                password=admin_password,
                                                tenant_id=tenant_id)
        auth_provider = AzureIdentityAuthenticationProvider(
            credential)
        self.adapter = GraphRequestAdapter(auth_provider)
        self.user_client = GraphServiceClient(self.adapter)

    async def get_service_principals(self):
      res = await self.user_client.service_principals.get()
      return res.value


async def run():
    graph = Graph()
    res = await graph.get_service_principals()
    print(res)

asyncio.run(run())

The following relevant packages are installed:

Package                              Version
------------------------------------ -----------
azure-core                           1.25.0
azure-identity                       1.12.0
microsoft-kiota-abstractions         0.4.0
microsoft-kiota-authentication-azure 0.2.0
microsoft-kiota-http                 0.4.0
microsoft-kiota-serialization-json   0.2.2
microsoft-kiota-serialization-text   0.2.0
msgraph-core                         1.0.0a4
msgraph-sdk                          1.0.0a9

Installation fails due to Windows MAX_PATH lenght

obviously this library ends up with a very long path. in case you cannot change the MAX_PATH in windows registry or group policy, you will not be able to install the package.

Lib\site-packages\msgraph\generated\groups\item\drives\item\items\item\get_activities_by_interval_with_start_date_time_with_end_date_time_with_interval\get_activities_by_interval_with_start_date_time_with_end_date_time_with_interval_request_builder.py

edit: sorry, was in the wrong repo. can anyone move it to pyton issue list? https://github.com/microsoftgraph/msgraph-sdk-python/issues

skiptoken for large request responses

I am looking to use the msgraph sdk for python. I want to get a list of devices in our tenant. We have thousands of devices. When using the msgraph.devices.get(), it will return 100 devices (the default limit for msgraph). Utilize the request_query=DevicesRequestBuilder.DevicesRequestBuilderGetQueryParameters(top=100,skip=100)
gives me this error msgraph.generated.models.o_data_errors.o_data_error.ODataError

After some digging in the Microsoft graph explorer for https://graph.microsoft.com/v1.0/devices?$top=100&$skip=100 gives this error

{ "error": { "code": "Request_BadRequest", "message": "'$skip' is not supported by the service.", "innerError": { "date": "2023-04-24T23:08:17", "request-id": "e077ef6b-d848-4fba-8cce-257e7188a6ec", "client-request-id": "4a9beda2-c3ee-0c9f-f012-5308f25f8954" } } }

So, skip is not supported for devices. It is the same in the beta requests.

Since I am able to get the data_data_next_link from my initial devices.get() response, I tried
request_query=DevicesRequestBuilder.DevicesRequestBuilderGetQueryParameters(top=100,skiptoken='RNFw.....')
However, I get an error TypeError: __init__() got an unexpected keyword argument skiptoken
After digging through the code, I modified device_request_builder.py, changing the DevicesRequestBuilderGetQueryParameters(): adding

 if original_name == "skiptoken":
       return "%24skiptoken
......
skiptoken: Optional[str] = None

and class DevicesRequestBuilder(): modifying

self.url_template: str = "{+baseurl}/devices{?%24top,%24skip,%24search,%24filter,%24count,%24orderby,%24select,%24expand,%24skiptoken}

These make the skiptoken available and work with GetQueryParameters.
So basically modifying this module to work with the limitations of msgraph

Is there another way to accomplish paging with devices in this python module?

If not, I can try a pull request for this.

Running sample code from README.md: RuntimeError: Event loop is closed

Python 3.9.9
msgraph-sdk 1.0.0a9

When I'm Running the sample code from chapter 3 in README.md the result is correct but additionally following error is returned:

Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x0000026331A6F1F0>
Traceback (most recent call last):
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 116, in __del__
    self.close()
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 108, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 746, in call_soon
    self._check_closed()
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 510, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x0000026331A6F1F0>
Traceback (most recent call last):
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 116, in __del__
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\proactor_events.py", line 108, in close
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 746, in call_soon
  File "C:\Users\SLEINEK\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 510, in _check_closed
RuntimeError: Event loop is closed
``'

Can someone help me out here?

Delete a Teams channel

Hello,

The issue #80 (comment) has been closed but I still have the same error. When doing:

# DELETE A TEAMS CHANNEL

async def delete_channel():
    try:
        await client.teams_by_id('TEAM_ID').channels_by_id('CHANNEL_ID').delete()
    except Exception as e:
        print(e.error.message)
asyncio.run(delete_channel())

I get the errror:

await client.teams_by_id(team_id).channels_by_id(channel_id).delete() AttributeError: 'ChannelItemRequestBuilder' object has no attribute 'delete'

The packages are already the same version as mentioned here https://github.com/microsoftgraph/msgraph-sdk-python/blob/main/Pipfile

Is the issue supposed to be fixed or is it still awaiting?
Or maybe these are not the right packages version?

Thank you in advance!

Broken pagination due to incorrect example and deserializer bug

Issue Description

I attempted to use the latest release (1.0.0a9) of msgraph-sdk for listing all available groups of a MS365 tenant with pagination. A quick look into the documentation showed that there is an example available on how to paginate.

My first attempt, based on the documentation available, resulted in this code snippet:

from msgraph.generated.groups.groups_request_builder import GroupsRequestBuilder
from msgraph.generated.models.group_collection_response import GroupCollectionResponse

async def test():
    query_params = GroupsRequestBuilder.GroupsRequestBuilderGetQueryParameters(
        select=['id', 'displayName', 'description'],
        top=2,
    )
    request_config = GroupsRequestBuilder.GroupsRequestBuilderGetRequestConfiguration(
        query_parameters=query_params,
    )

    groups = await client.groups.get(request_configuration=request_config)
    for group in groups.value:
        print(group.id, group.name, group.description)

    while groups.odata_next_link:
        request_info = client.groups.to_get_request_information(request_configuration=request_config)
        request_info.uri = groups.odata_next_link
        groups = request_adapter.send_async(request_info, GroupCollectionResponse)
        for group in groups.value:
            print(group.id, group.name, group.description)

Unfortunately this resulted in always only returning the first two groups instead of listing them all as expected. Further investigation on my side resulted in the following findings.

Identified Problems

Half-broken code for pagination example

The current code for the pagination example is somewhat broken:

  • The line print(msg.subject) at the end of the snippet is not properly indented
  • The call to request_adapter.send_async() does not use await despite calling an asynchronous function
  • The call to request_adapter.send_async() fails due requiring error mappings. Passing an empty dictionary as a third argument works, but seems wrong given that the usual get() method does specify error mappings. Am I supposed to manually replicate them?
  • The class MessageCollectionResponse is used without ever being imported anywhere and therefor does not work

I was able to come up with a solution for these issues myself, but this should really be fixed so that while pagination is still painful with the current library, it at least has a working example. I discovered though that even after all those fixes the while loop got never called due to odata_next_link being None, which leads me to the next issue.

Broken field deserialization of @odata.nextLink

The class property odata_next_link is always None, which itself returns the class attribute _odata_next_link which is also None. Simulating the same kind of call via the Graph Explorer shows the expected @odata.nextLink key within the response however.

Dumping the contents of the first groups response using __dict__ shows the following contents:

{'_additional_data': {'@odata.context': 'https://graph.microsoft.com/v1.0/$metadata#groups(id,displayName,description)', '@odata.next_link': 'https://graph.microsoft.com/v1.0/groups?$top=2&$select=id%2cdisplayName%2cdescription&$skiptoken=<redacted>'}, '_odata_count': None, '_odata_next_link': None, '_value': [<msgraph.generated.models.group.Group object at 0x10e4bde90>, <msgraph.generated.models.group.Group object at 0x10e4bde50>]}

This clearly shows that the response does contain the link to the next page, however it does not seem to be deserialized into _odata_next_link. I started debugging the library and noticed that the deserialization of @odata.nextLink is being configured as part of get_field_deserializes, which at a first glance seems to be correct.

I then however stumbled across _assign_field_values of the kiota-serialization-json-python library, which happens to transform all keys into snake-case before attempting to lookup the field deserializer. This means that while a deserializer is configured for @odata.nextLink, it is never considered after this transformation as Kiota is looking for @odata.next_link.

I confirmed this bug by manually editing base_collection_pagination_count_response.py and changing @odata.nextLink to @odata.next_link, after which the field was properly populated. Now I had a link to the next page, but still ended up with an infinite loop only returning the first two items over and over.

Incorrect class attribute uri in example

After digging some more through the code, I discovered another flaw in the pagination example code. Within the while loop, it advises the user to override request_info.uri with the link to the next page to properly paginate through the results.

This is incorrect though, as the actual attribute is named url and overriding uri does nothing, therefor resulting in an endless loop. To get this working, the line has to be changed to request_info.url = ....

Working Example

If anyone else happens to struggle with pagination, this is an actual working example:

from msgraph.generated.groups.groups_request_builder import GroupsRequestBuilder
from msgraph.generated.models.group_collection_response import GroupCollectionResponse

async def test():
    query_params = GroupsRequestBuilder.GroupsRequestBuilderGetQueryParameters(
        select=['id', 'displayName', 'description'],
        top=2,
    )
    request_config = GroupsRequestBuilder.GroupsRequestBuilderGetRequestConfiguration(
        query_parameters=query_params,
    )

    groups = await client.groups.get(request_configuration=request_config)
    for group in groups.value:
        print(group.id, group.name, group.description)

    while groups.odata_next_link:
        request_info = client.groups.to_get_request_information(request_configuration=request_config)
        request_info.url = groups.odata_next_link
        # Hint: Passing an empty error mapper might not be the ideal solution
        groups = await request_adapter.send_async(request_info, GroupCollectionResponse, {})
        for group in groups.value:
            print(group.id, group.name, group.description)

Please note that this still requires the bug regarding the @odata.nextLink deserialization to be fixed, as otherwise only the first page is being returned and the while loop will not be iterated even once.

Feedback

I understand that this library is still an alpha version, but the current way of handling pagination is unbelievably painful. The addition of an optional keyword argument to calls like .get(), e.g. next_link, would already greatly simplify paginating through objects, even without any custom page iterators. That way the user would just have to call .get(request_configuration=request_config, next_link=groups.odata_next_link) to obtain the next page.

With the current implementation though, this is not possible and the user has to manually cobble various internal bits and pieces together for somehow obtaining a working request. This is a very tedious ordeal and combined with broken documentation probably sheer impossible for less experienced users.

__path__ attribute not found on 'msgraph.generated.models.security'

I'm trying to use this sdk in a Django/Wagtail project.
Version: I installed the current version wich is 1.0.0a4 I believe.

It gets imported fast but if I import
from msgraph import GraphRequestAdapter, GraphServiceClient

I always get this error:
__path__ attribute not found on 'msgraph.generated.models.security' while trying to find 'msgraph.generated.models.security.cases_root'

I tried using an older version like 1.0.0a3 but than the import is very slow. Loading a project takes multiple minutes.
Any idea what is happening?

Get User by Id

I'm following the Build Python apps with Microsoft Graph and app-only authentication tutorial and I'm having difficulty figuring out how to return user information based on the user id with app-only authentication.

The examples in the README.md seem to leverage delegated authentication.

Looking at the UsersRequestBuilderGetQueryParameters method I can add a filter to search for email address, but returning user information by ID requires the ID at the root of the request, not a filter - docs:
/users/{id}

Can anyone help me identify an example of how to set the UserRequestBuilder to include the user's id?

Thank you,

Cody

msgraph.generated.models.o_data_errors.o_data_error.ODataError while listing emails

I am getting error "msgraph.generated.models.o_data_errors.o_data_error.ODataError"

graph.py

from configparser import SectionProxy
from azure.identity.aio import ClientSecretCredential
from kiota_authentication_azure.azure_identity_authentication_provider import (AzureIdentityAuthenticationProvider)
from msgraph import GraphRequestAdapter, GraphServiceClient
from msgraph.generated.me.messages.messages_request_builder import MessagesRequestBuilder
from msgraph.generated.users.users_request_builder import UsersRequestBuilder
class Graph:
    settings: SectionProxy
    client_credential: ClientSecretCredential
    adapter: GraphRequestAdapter
    app_client: GraphServiceClient

   def __init__(self, config: SectionProxy):
        self.settings = config
        client_id = self.settings['clientId']
        tenant_id = self.settings['tenantId']
        client_secret = self.settings['clientSecret']

        self.client_credential = ClientSecretCredential(tenant_id, client_id, client_secret)
        auth_provider = AzureIdentityAuthenticationProvider(self.client_credential)  # type: ignore
        self.adapter = GraphRequestAdapter(auth_provider)
        self.app_client = GraphServiceClient(self.adapter)

    async def get_inbox(self):
        query_params = MessagesRequestBuilder.MessagesRequestBuilderGetQueryParameters(
            # Only request specific properties
            select=['from', 'isRead', 'receivedDateTime', 'subject'],
            # Get at most 25 results
            top=25,
            # Sort by received time, newest first
            orderby=['receivedDateTime DESC']
        )
        request_config = MessagesRequestBuilder.MessagesRequestBuilderGetRequestConfiguration(
            query_parameters=query_params
        )

        messages = await self.app_client.me()\
            .mail_folders_by_id('inbox')\
            .messages()\
            .get(request_configuration=request_config)
        return messages

main.py

import asyncio
import configparser
from graph import Graph


async def main():
    print('Python Graph App-Only Tutorial\n')

    # Load settings
    config = configparser.ConfigParser()
    config.read(['config.cfg', 'config.dev.cfg'])
    azure_settings = config['azure']

    graph: Graph = Graph(azure_settings)

async def list_inbox(graph: Graph):
    message_page = await graph.get_inbox()
    if message_page is not None and message_page.value is not None:
        # Output each message's details
        for message in message_page.value:
            print('Message:', message.subject)
            if (
                message.from_escaped is not None and
                message.from_escaped.email_address is not None
            ):
                print('  From:', message.from_escaped.email_address.name or 'NONE')
            else:
                print('  From: NONE')
            print('  Status:', 'Read' if message.is_read else 'Unread')
            print('  Received:', message.received_date_time)

        # If @odata.nextLink is present
        more_available = message_page.odata_next_link is not None
        print('\nMore messages available?', more_available, '\n')

Error:

Traceback (most recent call last): File "C:\Users\xxx\PycharmProjects\MSGraph_App_Only\main.py", line 121, in <module> asyncio.run(main()) File "C:\Program Files\Python310\lib\asyncio\runners.py", line 44, in run return loop.run_until_complete(main) File "C:\Program Files\Python310\lib\asyncio\base_events.py", line 646, in run_until_complete return future.result() File "C:\Users\xxxx\PycharmProjects\MSGraph_App_Only\main.py", line 43, in main await list_inbox(graph) File "C:\Users\xxxxx\PycharmProjects\MSGraph_App_Only\main.py", line 80, in list_inbox message_page = await graph.get_inbox() File "C:\Users\xxxxxx\PycharmProjects\MSGraph_App_Only\graph.py", line 67, in get_inbox messages = await self.app_client.me()\ File "C:\Users\xxxxxx\PycharmProjects\MSGraph_App_Only\venv\lib\site-packages\msgraph\generated\me\mail_folders\item\messages\messages_request_builder.py", line 110, in get return await self.request_adapter.send_async(request_info, message_collection_response.MessageCollectionResponse, response_handler, error_mapping) File "C:\Users\xxxxxx\PycharmProjects\MSGraph_App_Only\venv\lib\site-packages\kiota_http\httpx_request_adapter.py", line 119, in send_async await self.throw_failed_responses(response, error_map) File "C:\Users\xxxxx\PycharmProjects\MSGraph_App_Only\venv\lib\site-packages\kiota_http\httpx_request_adapter.py", line 316, in throw_failed_responses raise error msgraph.generated.models.o_data_errors.o_data_error.ODataError

Expanding "members" property of a Group raise an exception

Hi

Looks like trying to retrieve the members of an Azure AD group with the group using the OData property expand raise an exception when the group contains at least 1 user:

INFO:azure.identity._internal.get_token_mixin:ClientSecretCredential.get_token succeeded
DEBUG:httpx._client:HTTP Request: GET https://graph.microsoft.com/beta/groups/983XXXXXXXXX5b6c?$expand=members "HTTP/1.1 200 OK"
NameError("name 'u_u_i_d' is not defined")
Traceback (most recent call last):
  File "./src/main.py", line 77, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()
  File "./src/main.py", line 29, in main
    await ring_manager.init()
  File "src/ringmanager.py", line 22, in init
    self.ring_groups = await self._load_ring_groups()
  File "src/ringmanager.py", line 55, in _load_ring_groups
    group = await self.msGraph.get_group_from_id(ring_id)
  File "src/graph.py", line 135, in get_group_from_id
    group = await self.client.groups_by_id(group_id).get(request_config)
  File ".venv/lib/python3.10/site-packages/msgraph/generated/groups/item/group_item_request_builder.py", line 252, in get
    return await self.request_adapter.send_async(request_info, group.Group, error_mapping)
  File ".venv/lib/python3.10/site-packages/kiota_http/httpx_request_adapter.py", line 122, in send_async
    result = root_node.get_object_value(model_type)
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 226, in get_object_value
    self._assign_field_values(result)
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 285, in _assign_field_values
    deserializer(JsonParseNode(val))
  File ".venv/lib/python3.10/site-packages/msgraph/generated/models/group.py", line 523, in <lambda>
    "members": lambda n : setattr(self, 'members', n.get_collection_of_object_values(directory_object.DirectoryObject)),
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 178, in get_collection_of_object_values
    return list(
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 180, in <lambda>
    lambda x: JsonParseNode(x).get_object_value(factory),  # type: ignore
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 226, in get_object_value
    self._assign_field_values(result)
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 285, in _assign_field_values
    deserializer(JsonParseNode(val))
  File ".venv/lib/python3.10/site-packages/msgraph/generated/models/user.py", line 1033, in <lambda>
    "assignedLicenses": lambda n : setattr(self, 'assigned_licenses', n.get_collection_of_object_values(assigned_license.AssignedLicense)),
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 178, in get_collection_of_object_values
    return list(
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 180, in <lambda>
    lambda x: JsonParseNode(x).get_object_value(factory),  # type: ignore
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 226, in get_object_value
    self._assign_field_values(result)
  File ".venv/lib/python3.10/site-packages/kiota_serialization_json/json_parse_node.py", line 285, in _assign_field_values
    deserializer(JsonParseNode(val))
  File ".venv/lib/python3.10/site-packages/msgraph/generated/models/assigned_license.py", line 73, in <lambda>
    "disabledPlans": lambda n : setattr(self, 'disabled_plans', n.get_collection_of_primitive_values(u_u_i_d)),
NameError: name 'u_u_i_d' is not defined

Repro steps:
Retrieve a group by id and specify the expand property with members value

query_params = GroupItemRequestBuilder.GroupItemRequestBuilderGetQueryParameters(expand=['members'])
request_config = GroupItemRequestBuilder.GroupItemRequestBuilderGetRequestConfiguration(query_parameters=query_params)
group = await self.client.groups_by_id(group_id).get(request_config)
return group

(self.client is a GraphServiceClient instance)

Removing the expand=['members'] property allow the group to be loaded correctly

How to implement ResponseHandler

I have an application that has to issue multiple requests to verify some items are located on OneDrive under certain folders for multiple items which causes a 429 (too many requests) response. According to the documentation (https://devblogs.microsoft.com/microsoft365dev/introducing-the-microsoft-graph-python-sdk-now-available-for-public-preview/#built-in-retry-handler) there is a built-in retry handler that will read the Retry-After header from the response and handle the waiting. I guess this would be passed to the get() call but I can't find anywhere that describes how to do this. Any chance you can post an example of how to do that?

Accessing user's OneDrive using application authentication

We have an application that needs to check if a file with a given name exists in OneDrive for a particular user and if so delete it. I've tried to get the list of drives [self.app_client.drives().get()] using the application authentication flow but getting back an OAuthData error (I can get the list of users using the same GraphServiceClient). I was wondering a) why the application is not able to get the list of drives and b) once I get the list of drives and see what the path is for the given user can the application impersonate that user so we can check their personal OneDrive for the file and delete it if necessary? This will have to be done without an interactive login as the application is a daemon process that runs in the background.

If possible a complete code sample would be very helpful. Appreciate it.

Unable to use workbook.

Trying to use .used_range fails to parse the response correctly.

Using the latest version, including all dependencies.

query = (
            self.user_client.drives_by_id(id=driveID)
            .items_by_id(id=itemID)
            .workbook.worksheets_by_id(id=woorksheetID)
            .used_range
        )

Results in this

File "C:\Program Files\Python311\Lib\site-packages\msgraph\generated\drives\item\items\item\workbook\worksheets\item\used_range\used_range_request_builder.py", line 58, in get
    return await self.request_adapter.send_async(request_info, workbook_range.WorkbookRange, error_mapping)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\site-packages\kiota_http\httpx_request_adapter.py", line 122, in send_async
    result = root_node.get_object_value(model_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\site-packages\kiota_serialization_json\json_parse_node.py", line 226, in get_object_value
    self._assign_field_values(result)
  File "C:\Program Files\Python311\Lib\site-packages\kiota_serialization_json\json_parse_node.py", line 286, in _assign_field_values
    deserializer(JsonParseNode(val))
  File "C:\Program Files\Python311\Lib\site-packages\msgraph\generated\models\workbook_range.py", line 260, in <lambda>
    "numberFormat": lambda n : setattr(self, 'number_format', n.get_object_value(json.Json)),
                                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\site-packages\kiota_serialization_json\json_parse_node.py", line 226, in get_object_value
    self._assign_field_values(result)
  File "C:\Program Files\Python311\Lib\site-packages\kiota_serialization_json\json_parse_node.py", line 283, in _assign_field_values
    for key, val in object_dict.items():
                    ^^^^^^^^^^^^^^^^^

Long running operations Support

In Azure SDK/API as an example,
Long running operations are performed in the following flow?

  1. A URL is returned that can be used to retrieve the result of the operation once completed.
  2. The URL can be used to monitor the status of the operation, at regular intervals until it is completed.
  3. Optionally a callback URL is availed that can be used to get notification once a long running operation is completed.

Technically, this looks like polling and a callback URL.

Do we have a spec on this for Graph SDKs @baywet @andrueastman @darrelmiller @ddyett @Ndiritu @samwelkanda

We should align with other languages and make initialization easier

As discussed, we'd like to abstract these 3 lines of code:

auth_provider = AzureIdentityAuthenticationProvider(credential, scopes=scopes)

# Initialize a request adapter. Handles the HTTP concerns
request_adapter = GraphRequestAdapter(auth_provider)

# Get a service client
client = GraphServiceClient(request_adapter)

And only request from the developer to specify the context, the scope and its required values.

Multiple asyncio runs cause SSLWantReadError

Hi,

I would like to use the Microsoft graph SDK for Python with Django DB transaction.
As Django DB transaction with transaction.atomic() must be in sync context, I must use multiple asyncio.run calls.

The question is how can we mix async graph requests and sync calls with multiple async calls?

The first asyncio.run works correctly, I am able to retrieve two MailFolder with their ID and messages in one of these folders.

mailfolders = await asyncio.gather(
    self._retrieve_mailfolder_source(),
    self._retrieve_mailfolder_backup(),
    return_exceptions=False
)

# Same async event loop, no SSLWantReadError raised
result = await asyncio.gather(self._get_messages(mailfolders[0]))

The issue is for the second asyncio.run, the second call raise a SSLWantReadError exception.

For your information, when I run all graph requests in the same event loop, it works.
I don't know why with a second/different event loop, the graph request causes SSLWantReadError error.

It should be possible to execute many async calls with asyncio.run, correct?

def _retrieve_mailfolders(self):
    mailfolders = await asyncio.gather(
        self._retrieve_mailfolder_source(),
        self._retrieve_mailfolder_backup(),
        return_exceptions=False
    )
    return mailfolders

def _retrieve(self):
    mailfolders = asyncio.run(self._retrieve_mailfolders())
    
    with transaction.atomic():
        # Second async run causes SSLWantReadError
        messages = asyncio.run(self._get_messages(mailfolders[0]))

Exception

Scopes:
('https://graph.microsoft.com/.default',)

Token:
AccessToken(token='eyJ0eXAiOiJKV1QiLCJub25jZSI6Imo*********************************', expires_on=1675276095)

Successfully authenticated to Azure



Traceback (most recent call last):
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/anyio/streams/tls.py", line 130, in _call_sslobject_method
    result = func(*args)
  File "/usr/lib/python3.10/ssl.py", line 917, in read
    v = self._sslobj.read(len)
ssl.SSLWantReadError: The operation did not complete (read) (_ssl.c:2548)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/http11.py", line 91, in handle_async_request
    ) = await self._receive_response_headers(**kwargs)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/http11.py", line 155, in _receive_response_headers
    event = await self._receive_event(timeout=timeout)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/http11.py", line 191, in _receive_event
    data = await self._network_stream.read(
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 34, in read
    return await self._stream.receive(max_bytes=max_bytes)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/anyio/streams/tls.py", line 195, in receive
    data = await self._call_sslobject_method(self._ssl_object.read, max_bytes)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/anyio/streams/tls.py", line 137, in _call_sslobject_method
    data = await self.transport_stream.receive()
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 1264, in receive
    self._transport.resume_reading()
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 814, in resume_reading
    self._add_reader(self._sock_fd, self._read_ready)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 760, in _add_reader
    self._loop._add_reader(fd, callback, *args)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 253, in _add_reader
    self._check_closed()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/app_discover/core/adapters/emailMsGraph/msGraphAdapter.py", line 314, in _move_message
    message = await self._msgraph_client.me.messages_by_id(message.id).move.post(
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/msgraph/generated/me/messages/item/move/move_request_builder.py", line 58, in post
    return await self.request_adapter.send_async(request_info, message.Message, error_mapping)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/httpx_request_adapter.py", line 112, in send_async
    response = await self.get_http_response_message(request_info)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/httpx_request_adapter.py", line 320, in get_http_response_message
    resp = await self._http_client.send(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpx/_client.py", line 1620, in send
    response = await self._send_handling_auth(
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpx/_client.py", line 1648, in _send_handling_auth
    response = await self._send_handling_redirects(
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpx/_client.py", line 1685, in _send_handling_redirects
    response = await self._send_single_request(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpx/_client.py", line 1722, in _send_single_request
    response = await transport.handle_async_request(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/msgraph_core/middleware/async_graph_transport.py", line 21, in handle_async_request
    response = await self.pipeline.send(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 31, in send
    return await self._first_middleware.send(request, self._transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/redirect_handler.py", line 65, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 57, in send
    return await self.next.send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/retry_handler.py", line 77, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 57, in send
    return await self.next.send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/parameters_name_decoding_handler.py", line 48, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 57, in send
    return await self.next.send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/msgraph_core/middleware/telemetry.py", line 48, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 54, in send
    response = await transport.handle_async_request(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/msgraph_core/middleware/async_graph_transport.py", line 21, in handle_async_request
    response = await self.pipeline.send(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 31, in send
    return await self._first_middleware.send(request, self._transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/redirect_handler.py", line 65, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 57, in send
    return await self.next.send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/retry_handler.py", line 77, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 57, in send
    return await self.next.send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/parameters_name_decoding_handler.py", line 48, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 57, in send
    return await self.next.send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/msgraph_core/middleware/telemetry.py", line 48, in send
    response = await super().send(request, transport)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/kiota_http/middleware/middleware.py", line 54, in send
    response = await transport.handle_async_request(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpx/_transports/default.py", line 353, in handle_async_request
    resp = await self._pool.handle_async_request(req)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 253, in handle_async_request
    raise exc
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 237, in handle_async_request
    response = await connection.handle_async_request(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/connection.py", line 90, in handle_async_request
    return await self._connection.handle_async_request(request)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/http11.py", line 111, in handle_async_request
    await self._response_closed()
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/http11.py", line 224, in _response_closed
    await self.aclose()
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/_async/http11.py", line 232, in aclose
    await self._network_stream.aclose()
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 54, in aclose
    await self._stream.aclose()
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/anyio/streams/tls.py", line 192, in aclose
    await self.transport_stream.aclose()
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 1323, in aclose
    self._transport.close()
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 706, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 753, in call_soon
    self._check_closed()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/manage.py", line 22, in <module>
    main()
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/django/core/management/base.py", line 402, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/renji/virtualenvs/f5/maintenanceManager_3.10/lib/python3.10/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/app_discover/management/commands/discover.py", line 109, in handle
    raise e
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/app_discover/management/commands/discover.py", line 103, in handle
    messages = adapter.retrieve(store, range)
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/app_discover/core/adapters/emailMsGraph/msGraphAdapter.py", line 358, in retrieve
    raise e
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/app_discover/core/adapters/emailMsGraph/msGraphAdapter.py", line 355, in retrieve
    return self._retrieve(store, limit)
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/app_discover/core/adapters/emailMsGraph/msGraphAdapter.py", line 452, in _retrieve
    move, = asyncio.run(self._move_message(message, mailfolder_backup))
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/home/renji/scripts/f5/circuitMaintenanceParser/src/app_discover/core/adapters/emailMsGraph/msGraphAdapter.py", line 321, in _move_message
    raise DiscoverAdapterError(
app_discover.errors.DiscoverAdapterError: Unable to move the message 'TCL TT: "*******" / "Emergency" / "Scheduled" / Planned Work Notification' to the mail folder 'Backup':
Event loop is closed

Environment

Python version 3.10.9 (Ubuntu 20.04)

  • azure-core : 1.26.2
  • azure-identity : 1.12.0
  • microsoft-kiota-abstractions : 0.3.0
  • microsoft-kiota-authentication-azure : 0.2.0
  • microsoft-kiota-http : 0.3.1
  • microsoft-kiota-serialization-json : 0.2.1
  • microsoft-kiota-serialization-text : 0.2.0
  • msgraph-core : 1.0.0a3
  • msgraph-sdk : 1.0.0a9

Unable to use worksheet.used_range_values_only

Using the latest version of msgraph-sdk

When trying to use the .used_ranges_values_only like this.

query = (
            self.user_client.drives_by_id(id=driveID)
            .items_by_id(id=itemID)
            .workbook.worksheets_by_id(id=woorksheetID)
            .used_range_with_values_only(True)
        )

This error message is thrown

  File "my project file", line 242, in get_file
    .used_range_with_values_only(True)
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\site-packages\msgraph\generated\drives\item\items\item\workbook\worksheets\item\workbook_worksheet_item_request_builder.py", line 274, in used_range_with_values_only
    return used_range_with_values_only_request_builder.UsedRangeWithValuesOnlyRequestBuilder(self.request_adapter, self.path_parameters, values_only)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\site-packages\msgraph\generated\drives\item\items\item\workbook\worksheets\item\used_range_with_values_only\used_range_with_values_only_request_builder.py", line 36, in __init__
    url_tpl_params[""] = valuesOnly
                         ^^^^^^^^^^
NameError: name 'valuesOnly' is not defined. Did you mean: 'values_only'?

This is caused because the request query is using an invalid variable.

https://github.com/microsoftgraph/msgraph-sdk-python/blob/main/msgraph/generated/drives/item/items/item/workbook/worksheets/item/used_range_with_values_only/used_range_with_values_only_request_builder.py#L33-L36

Note: There are three things wrong here.

  1. url_tpl_params[""] is not setting a parameter name and so won't be included in the query URL.
  2. valuesOnly needs to be changed to values_only (or values_only renamed to valuesOnly)
  3. values_only is currently a python bool and will be formatted into a string as True. The graph API does not support the value of True, and will only accept true. The value here will need to be converted from True to true before the request is finished.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.