fix: build BatchRequestItem urls relative to the API version - #1118
Max Azatian (HardMax71) wants to merge 1 commit into
Conversation
BatchRequestItem kept the absolute request URL, and set_url() could not strip the version because its regex only matched //v1.0/ (a JavaScript regex literal ported as-is). The constructor now goes through set_url(), and the regex matches /v1.0 or /beta as a path segment. Fixes microsoftgraph#1116.
|
@microsoft-github-policy-service agree |
|
Any chance this gets a review? The workflows on this PR are still waiting for approval, so Build and test has not run here; locally the full suite passes on the PR head. The branch is two workflow-only commits behind main and merges cleanly, and I can rebase if you prefer. Still reproduces on 1.5.1 with msgraph-sdk 1.63.0. It is easy to miss: a fresh request builder gives a relative URL, and the absolute one only appears after a request has been sent through the same builder chain, because the adapter writes repro# pip install msgraph-sdk==1.63.0 Deprecated (Deprecated is the missing dependency from #1030)
import asyncio
from importlib.metadata import version
import httpx
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from msgraph import GraphRequestAdapter, GraphServiceClient
from msgraph.generated.users.item.messages.item.move.move_post_request_body import MovePostRequestBody
from msgraph_core import GraphClientFactory
from msgraph_core.requests.batch_request_item import BatchRequestItem
async def main():
print("msgraph-core", version("msgraph-core"), "msgraph-sdk", version("msgraph-sdk"))
transport = httpx.MockTransport(lambda request: httpx.Response(200, json={"value": []}))
http = GraphClientFactory.create_with_default_middleware(client=httpx.AsyncClient(transport=transport))
client = GraphServiceClient(request_adapter=GraphRequestAdapter(AnonymousAuthenticationProvider(), client=http))
user = client.users.by_user_id("u1")
def item_url():
info = user.messages.by_message_id("m1").move.to_post_request_information(MovePostRequestBody(destination_id="f1"))
return BatchRequestItem(request_information=info, id="m1").url
print("before any request:", item_url())
await user.messages.get()
print("after user.messages.get():", item_url())
asyncio.run(main())1.5.1: this PR: |



Overview
Fixes #1116.
BatchRequestItembuilt from aRequestInformationkept the fullhttps://graph.microsoft.com/v1.0/...URL, so the serialized$batchbody carried absolute URLs.set_url()is meant to strip the version, butAPI_VERSION_REGEX = re.compile(r'/\/(v1.0|beta)/')reads as a JavaScript regex literal ported as-is: in Python both/and\/are literal slashes, so it only ever matched//v1.0/.Three lines: the regex is now
r'/(v1\.0|beta)(?=/|$)'(matches the version as a whole path segment, so/sites/beta-siteis left alone),ME_TOKEN_REGEXgets the same treatment, and the constructor goes throughset_url(), which already handles the me-token, query string and fragment. The result matches what the JavaScript SDK does inBatchRequestContent.ts(it strips scheme, host and version, and its tests expect/me/drive/root/children).Notes
The existing tests pinned the old behaviour (
url == base_urlafter construction,"/v1.0/me"afterset_url), so those assertions changed to the relative form. Anyone who readitem.urlback and stripped the version themselves, as we did, gets an already relative URL now; the batch body they send does not change. Open PRs #711 and #996 touchserialize()in the same file, not these lines.Testing Instructions
pip install -r requirements-dev.txt, thenpytest tests/requests/test_batch_request_item.py: 3 new cases (beta URL with query and fragment, an already relative path, a version-like segment inside the path), 7 updated ones.yapf -dr src,mypy srcandpylint src --disable=W --rcfile=.pylintrcclean.POST .../v1.0/users/u1/messages/m1/moveitem: the serialized body'surlgoes from the absolute URL to/users/u1/messages/m1/move.