Skip to content

feat: refactor user management API to RESTful conventions#1980

Open
NJX-njx wants to merge 1 commit intovllm-project:mainfrom
NJX-njx:feat/1640-restful-user-api
Open

feat: refactor user management API to RESTful conventions#1980
NJX-njx wants to merge 1 commit intovllm-project:mainfrom
NJX-njx:feat/1640-restful-user-api

Conversation

@NJX-njx
Copy link

@NJX-njx NJX-njx commented Mar 4, 2026

Summary

Fixes #1640

Refactor the metadata user management API from non-standard POST-based endpoints to proper RESTful conventions while maintaining full backward compatibility.

Changes

New RESTful endpoints

Method Endpoint Description
POST /users Create a new user
GET /users/{name} Read user by name
PUT /users/{name} Update user by name
DELETE /users/{name} Delete user by name

Legacy endpoints (deprecated, kept for backward compatibility)

The old POST-based endpoints (/CreateUser, /ReadUser, /UpdateUser, /DeleteUser) are preserved via a separate legacy_router and delegate to the new RESTful implementations. They emit deprecation warnings.

Files Changed

  • python/aibrix/aibrix/metadata/api/v1/users.py: New RESTful router + legacy router with deprecation warnings
  • python/aibrix/aibrix/metadata/app.py: Mount both routers
  • python/aibrix/tests/metadata/test_users_api.py: Tests for both RESTful and legacy endpoints

Refactor the metadata user management API from non-standard POST-based
endpoints to proper RESTful conventions while maintaining full backward
compatibility.

New RESTful endpoints:
- POST   /users          - Create a new user
- GET    /users/{name}   - Read user by name
- PUT    /users/{name}   - Update user by name
- DELETE /users/{name}   - Delete user by name

Legacy endpoints (deprecated, kept for backward compatibility):
- POST /CreateUser -> delegates to POST /users
- POST /ReadUser   -> delegates to GET /users/{name}
- POST /UpdateUser -> delegates to PUT /users/{name}
- POST /DeleteUser -> delegates to DELETE /users/{name}

Tests updated to cover both RESTful and legacy endpoints.

Fixes vllm-project#1640
Copilot AI review requested due to automatic review settings March 4, 2026 10:30
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request modernizes the metadata user management API by transitioning from non-standard POST-based endpoints to a fully RESTful design. The core change involves implementing new, semantically clear HTTP methods and URL structures for user operations. Crucially, existing integrations are safeguarded through the introduction of a backward-compatible legacy router that delegates to the new implementations while issuing deprecation warnings, ensuring a smooth transition for consumers of the API.

Highlights

  • RESTful API Refactoring: The user management API has been refactored to adhere to RESTful conventions, introducing new endpoints for user operations.
  • New RESTful Endpoints: New endpoints include POST /users for creation, GET /users/{name} for reading, PUT /users/{name} for updating, and DELETE /users/{name} for deletion.
  • Backward Compatibility: Legacy POST-based endpoints (e.g., /CreateUser) are preserved via a dedicated legacy router to ensure backward compatibility.
  • Deprecation Warnings: Calls to the deprecated legacy endpoints now emit deprecation warnings to guide users towards the new RESTful API.
  • Expanded Test Coverage: Tests have been updated and expanded to cover both the new RESTful API endpoints and the backward-compatible legacy endpoints.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • python/aibrix/aibrix/metadata/api/v1/users.py
    • Imported the 'warnings' module.
    • Defined a new 'legacy_router' for deprecated endpoints.
    • Converted existing user CRUD functions to new RESTful endpoints (/users, /users/{name}) using appropriate HTTP methods (GET, POST, PUT, DELETE).
    • Implemented legacy endpoint functions (/CreateUser, /ReadUser, /UpdateUser, /DeleteUser) that call the new RESTful functions and issue DeprecationWarnings.
    • Adjusted parameter handling for user name from request body to URL path for GET, PUT, DELETE operations.
  • python/aibrix/aibrix/metadata/app.py
    • Mounted the newly created 'legacy_router' alongside the main 'users.router'.
    • Updated the log message to reflect the mounting of both RESTful and legacy user API endpoints.
  • python/aibrix/tests/metadata/test_users_api.py
    • Updated the test coverage description to explicitly mention RESTful and legacy APIs.
    • Renamed the main test class from 'TestUsersAPI' to 'TestUsersRESTfulAPI'.
    • Modified existing tests within 'TestUsersRESTfulAPI' to target the new RESTful endpoints and their respective HTTP methods.
    • Added a new test class 'TestUsersLegacyAPI' to specifically test the functionality and deprecation warnings of the old POST-based endpoints.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the user management API to follow RESTful conventions while preserving deprecated legacy POST endpoints for backward compatibility.

Changes:

  • Introduced RESTful /users and /users/{name} endpoints using GET/POST/PUT/DELETE.
  • Added a legacy_router that preserves /CreateUser, /ReadUser, /UpdateUser, /DeleteUser and delegates to RESTful handlers.
  • Updated and expanded tests to cover both RESTful and legacy endpoints.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

File Description
python/aibrix/aibrix/metadata/api/v1/users.py Adds RESTful routes, introduces a legacy router with deprecation warnings, and adjusts delete response payload
python/aibrix/aibrix/metadata/app.py Mounts both RESTful and legacy user routers
python/aibrix/tests/metadata/test_users_api.py Updates tests for RESTful endpoints and adds coverage for legacy endpoints

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +207 to +208
logger.info(f"Deleted user: {name}")
return UserResponse(message=f"Deleted User: {name}", user=None)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

This changes legacy /DeleteUser behavior: it now returns user=None via delegation to delete_user, whereas the previous implementation returned the deleted user in the user field. That breaks the PR’s stated ‘full backward compatibility’. Consider keeping the RESTful DELETE /users/{name} response as-is, but have legacy_delete_user return UserResponse(..., user=user) (or reconstruct the prior payload) to preserve the legacy contract.

Copilot uses AI. Check for mistakes.
DeprecationWarning,
stacklevel=1,
)
return await delete_user(request, user.name)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

This changes legacy /DeleteUser behavior: it now returns user=None via delegation to delete_user, whereas the previous implementation returned the deleted user in the user field. That breaks the PR’s stated ‘full backward compatibility’. Consider keeping the RESTful DELETE /users/{name} response as-is, but have legacy_delete_user return UserResponse(..., user=user) (or reconstruct the prior payload) to preserve the legacy contract.

Suggested change
return await delete_user(request, user.name)
# Legacy behavior: return the deleted user in the response.
# First, read the user (will raise 404 if it does not exist).
existing_user = await get_user(request, user.name)
# Then perform the deletion using the RESTful handler.
delete_result = await delete_user(request, user.name)
# Return a response that preserves the legacy contract: include the
# deleted user in the `user` field while keeping the delete message.
return UserResponse(message=delete_result.message, user=existing_user.user)

Copilot uses AI. Check for mistakes.
Comment on lines +145 to +146
@router.put("/users/{name}")
async def update_user(request: Request, name: str, user: User) -> UserResponse:
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Silently overriding user.name with the path parameter can hide client mistakes (e.g., sending mismatched names) and makes the request semantics ambiguous. Prefer either (a) validating that user.name matches name and returning a 400 on mismatch, or (b) using a different request model for updates that omits name entirely so the path is the single source of truth.

Copilot uses AI. Check for mistakes.
Comment on lines +171 to +172
# Use the name from URL path, override body if different
user.name = name
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Silently overriding user.name with the path parameter can hide client mistakes (e.g., sending mismatched names) and makes the request semantics ambiguous. Prefer either (a) validating that user.name matches name and returning a 400 on mismatch, or (b) using a different request model for updates that omits name entirely so the path is the single source of truth.

Suggested change
# Use the name from URL path, override body if different
user.name = name
# Validate that the name in the body matches the path parameter
if user.name != name:
logger.warning(
f"Name mismatch in update_user: path name='{name}', body name='{user.name}'"
)
raise HTTPException(
status_code=400,
detail="User name in path does not match user name in request body",
)

Copilot uses AI. Check for mistakes.
Comment on lines +219 to +223
warnings.warn(
"POST /CreateUser is deprecated, use POST /users instead",
DeprecationWarning,
stacklevel=1,
)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Using warnings.warn(..., DeprecationWarning) inside an API handler is easy to miss in production because DeprecationWarning is filtered out by default in many runtime configurations, and it is not visible to API consumers. If the goal is to ‘emit deprecation warnings’ to clients, consider adding an HTTP Warning/Deprecation signal (e.g., response headers) and/or logging via logger.warning. If you keep warnings.warn, stacklevel should generally be 2 so the warning points at the legacy endpoint wrapper rather than the warnings.warn line.

Copilot uses AI. Check for mistakes.
Comment on lines +236 to +237
def test_legacy_create_user(self, app_with_redis, mock_redis):
"""Test legacy POST /CreateUser still works."""
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Legacy endpoint tests verify functional behavior, but they don’t assert that a deprecation signal is actually emitted (which is part of the PR description). Once the implementation is finalized (e.g., response header, logger.warning, or warnings.warn), add assertions using pytest.warns, caplog, or header checks so regressions don’t silently remove the deprecation notice.

Copilot uses AI. Check for mistakes.
Comment on lines +244 to +245
response = client.post("/CreateUser", json=user_data)
assert response.status_code == 200
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Legacy endpoint tests verify functional behavior, but they don’t assert that a deprecation signal is actually emitted (which is part of the PR description). Once the implementation is finalized (e.g., response header, logger.warning, or warnings.warn), add assertions using pytest.warns, caplog, or header checks so regressions don’t silently remove the deprecation notice.

Copilot uses AI. Check for mistakes.
Comment on lines +214 to +235
@pytest.fixture
def mock_redis(self):
"""Mock Redis client."""
mock = AsyncMock()
return mock

@pytest.fixture
def app_with_redis(self, mock_redis):
"""Build app with mocked Redis."""
from argparse import Namespace

args = Namespace(
enable_fastapi_docs=False,
disable_batch_api=True,
disable_file_api=True,
enable_k8s_job=False,
e2e_test=False,
)
app = build_app(args)
app.state.redis_client = mock_redis
return app

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

The mock_redis and app_with_redis fixtures are duplicated across TestUsersRESTfulAPI and TestUsersLegacyAPI. Consider moving them to module-scoped fixtures (or a shared base class) to reduce repetition and keep future setup changes in one place.

Suggested change
@pytest.fixture
def mock_redis(self):
"""Mock Redis client."""
mock = AsyncMock()
return mock
@pytest.fixture
def app_with_redis(self, mock_redis):
"""Build app with mocked Redis."""
from argparse import Namespace
args = Namespace(
enable_fastapi_docs=False,
disable_batch_api=True,
disable_file_api=True,
enable_k8s_job=False,
e2e_test=False,
)
app = build_app(args)
app.state.redis_client = mock_redis
return app

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +221
@pytest.fixture
def app_with_redis(self, mock_redis):
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

The mock_redis and app_with_redis fixtures are duplicated across TestUsersRESTfulAPI and TestUsersLegacyAPI. Consider moving them to module-scoped fixtures (or a shared base class) to reduce repetition and keep future setup changes in one place.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request successfully refactors the user management API to adhere to RESTful conventions while ensuring full backward compatibility. New RESTful endpoints (/users with POST, GET, PUT, DELETE) have been introduced, and the old POST-based endpoints (/CreateUser, /ReadUser, /UpdateUser, /DeleteUser) are preserved via a legacy_router and emit deprecation warnings. The changes are well-tested, the code is clean, and documentation is updated. However, a high-severity Broken Access Control vulnerability has been identified: both the new and legacy endpoints lack authentication or authorization checks, allowing unauthorized users to manage rate limiting configurations for any user in the system. It is strongly recommended to implement an authentication and authorization layer before deploying these changes.

Comment on lines +216 to +257
@legacy_router.post("/CreateUser")
async def legacy_create_user(request: Request, user: User) -> UserResponse:
"""Create a new user. Deprecated: use POST /users instead."""
warnings.warn(
"POST /CreateUser is deprecated, use POST /users instead",
DeprecationWarning,
stacklevel=1,
)
return await create_user(request, user)


@legacy_router.post("/ReadUser")
async def legacy_read_user(request: Request, user: User) -> UserResponse:
"""Read user information. Deprecated: use GET /users/{name} instead."""
warnings.warn(
"POST /ReadUser is deprecated, use GET /users/{name} instead",
DeprecationWarning,
stacklevel=1,
)
return await get_user(request, user.name)


@legacy_router.post("/UpdateUser")
async def legacy_update_user(request: Request, user: User) -> UserResponse:
"""Update user information. Deprecated: use PUT /users/{name} instead."""
warnings.warn(
"POST /UpdateUser is deprecated, use PUT /users/{name} instead",
DeprecationWarning,
stacklevel=1,
)
return await update_user(request, user.name, user)


@legacy_router.post("/DeleteUser")
async def legacy_delete_user(request: Request, user: User) -> UserResponse:
"""Delete a user. Deprecated: use DELETE /users/{name} instead."""
warnings.warn(
"POST /DeleteUser is deprecated, use DELETE /users/{name} instead",
DeprecationWarning,
stacklevel=1,
)
return await delete_user(request, user.name)
Copy link
Contributor

Choose a reason for hiding this comment

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

security-high high

The legacy endpoints are also unauthenticated and unauthorized, maintaining the security risk for backward-compatible clients.

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.

refine the user management API

2 participants