Skip to content

fix: build BatchRequestItem urls relative to the API version - #1118

Merged
Vincent Biret (baywet) merged 3 commits into
microsoftgraph:mainfrom
HardMax71:fix/batch-request-item-relative-url
Oct 26, 2026
Merged

Vincent Biret (baywet) merged 3 commits into
microsoftgraph:mainfrom
HardMax71:fix/batch-request-item-relative-url

Conversation

@HardMax71

@HardMax71 Max Azatian (HardMax71) commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Fixes #1116. A BatchRequestItem built from a RequestInformation kept the full https://graph.microsoft.com/v1.0/... URL, so the $batch body carried absolute URLs. set_url() should strip the version, but API_VERSION_REGEX = re.compile(r'/\/(v1.0|beta)/') is a JavaScript regex literal ported as-is: in Python both / and \/ are literal slashes, so it only matched //v1.0/.

The regex is now r'^/(v1\.0|beta)(?=/|$)', the version as the first path segment only, so /sites/beta-site and /sites/beta/lists stay. ME_TOKEN_REGEX gets the same fix and set_url() uses it. The constructor goes through set_url(), which already handles the me-token, query and fragment. This matches the JavaScript SDK, whose tests expect /me/drive/root/children.

Notes

Tests that pinned the old behaviour (url == base_url, "/v1.0/me") now expect the relative form. Code that stripped the version from item.url itself now gets a relative URL; the batch body it sends doesn't change. Open PRs #711 and #996 touch serialize(), not these lines.

Testing Instructions

  • pytest tests/requests/test_batch_request_item.py after pip install -r requirements-dev.txt: 4 new cases (beta URL with query and fragment, relative path, version-like segment, beta after the first segment), 7 updated.
  • Full suite: 88 passed; yapf -dr src, mypy src, pylint src --disable=W --rcfile=.pylintrc clean.
  • A POST .../v1.0/users/u1/messages/m1/move item now serializes url as /users/u1/messages/m1/move.

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.
Copilot AI lite review requested due to automatic review settings September 4, 2026 07:44
@HardMax71
Max Azatian (HardMax71) requested a review from a team as a code owner September 4, 2026 07:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

@HardMax71

Copy link
Copy Markdown
Contributor Author

Max Azatian (Max Azatian (@HardMax71)) please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"

Contributor License Agreement

@microsoft-github-policy-service agree

@HardMax71

Copy link
Copy Markdown
Contributor Author

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 baseurl into the shared path parameters.

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:

before any request: /users/u1/messages/m1/move
after user.messages.get(): https://graph.microsoft.com/v1.0/users/u1/messages/m1/move

this PR:

before any request: /users/u1/messages/m1/move
after user.messages.get(): /users/u1/messages/m1/move

@HardMax71

Copy link
Copy Markdown
Contributor Author

Vincent Biret (@baywet) could you review this when you have time? It builds BatchRequestItem URLs relative to the API version.

@baywet

Copy link
Copy Markdown
Member

I can't, be Peter Ombwa (@peombwa) should be able to help here.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The URL matcher can remove valid path segments from relative URLs, and the edited ME_TOKEN_REGEX remains unused.

1 open issue

Review effort: Lite

Comment thread src/msgraph_core/requests/batch_request_item.py Outdated
set_url() also runs on relative URLs, and the unanchored pattern turned /sites/beta/lists into /sites/lists. ME_TOKEN_REGEX is now the pattern set_url() applies, instead of a second copy of the literal.
auto-merge was automatically disabled October 26, 2026 17:26

Head branch was pushed to by a user without write access

@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

ME_TOKEN_REGEX can replace a prefix of longer path segments; it needs a path-segment boundary.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Require path-segment boundary for ME_TOKEN_REGEX

src/​msgraph_core/​requests/​batch_request_item.py:25

ME_TOKEN_REGEX matches the placeholder as a prefix rather than as a complete path segment. A legitimate URL such as /users/me-token-to-replace-extra is therefore rewritten to /me-extra, and the same can happen when that string is part of a longer user-id segment. Add a path-segment boundary, as was done for the API-version regex, so only the exact placeholder segment is replaced.

1 resolved since last review

Review effort: Lite

@baywet
Vincent Biret (baywet) merged commit 20beb4b into microsoftgraph:main Oct 26, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BatchRequestItem keeps the absolute URL, and set_url() only matches //v1.0/

4 participants