Skip to content

Commit 03b230e

Browse files
committed
feat: enhance Google OAuth login functionality and update API documentation
- Added support for an optional `extension_callback` parameter in the Google login API. - Updated OpenAPI documentation to reflect the new query parameter and improved error handling for validation errors. - Refactored related types and SDK generation to accommodate the new login data structure. - Removed unused utility exports from the client codebase for better clarity and maintainability.
1 parent bd74a2b commit 03b230e

File tree

21 files changed

+153
-112
lines changed

21 files changed

+153
-112
lines changed

admin/src/client/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export { CancelablePromise, CancelError } from "./core/CancelablePromise"
44
export { OpenAPI, type OpenAPIConfig } from "./core/OpenAPI"
55
export * from "./sdk.gen"
66
export * from "./types.gen"
7+
export * from "./types"
78
export * from "./utils"

admin/src/client/sdk.gen.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import type { CancelablePromise } from './core/CancelablePromise';
44
import { OpenAPI } from './core/OpenAPI';
55
import { request as __request } from './core/request';
6-
import type { GoogleOauthGoogleCallbackApiData, GoogleOauthGoogleCallbackApiResponse, GoogleOauthGoogleLoginResponse, GoogleOauthGoogleCallbackData, GoogleOauthGoogleCallbackResponse, HealthGetHealthApiResponse, ItemsReadItemsData, ItemsReadItemsResponse, ItemsCreateItemData, ItemsCreateItemResponse, ItemsReadItemData, ItemsReadItemResponse, ItemsUpdateItemData, ItemsUpdateItemResponse, ItemsDeleteItemData, ItemsDeleteItemResponse, LoginLoginAccessTokenData, LoginLoginAccessTokenResponse, LoginTestTokenResponse, LoginLogoutResponse, LoginRecoverPasswordData, LoginRecoverPasswordResponse, LoginResetPasswordData, LoginResetPasswordResponse, LoginRecoverPasswordHtmlContentData, LoginRecoverPasswordHtmlContentResponse, PrivateCreateUserData, PrivateCreateUserResponse, UsersReadUsersData, UsersReadUsersResponse, UsersCreateUserData, UsersCreateUserResponse, UsersReadUserMeResponse, UsersDeleteUserMeResponse, UsersUpdateUserMeData, UsersUpdateUserMeResponse, UsersUpdatePasswordMeData, UsersUpdatePasswordMeResponse, UsersRegisterUserData, UsersRegisterUserResponse, UsersReadUserByIdData, UsersReadUserByIdResponse, UsersUpdateUserData, UsersUpdateUserResponse, UsersDeleteUserData, UsersDeleteUserResponse, UtilsTestEmailData, UtilsTestEmailResponse, UtilsHealthCheckResponse } from './types.gen';
6+
import type { GoogleOauthGoogleCallbackApiData, GoogleOauthGoogleCallbackApiResponse, GoogleOauthGoogleLoginData, GoogleOauthGoogleLoginResponse, GoogleOauthGoogleCallbackData, GoogleOauthGoogleCallbackResponse, HealthGetHealthApiResponse, ItemsReadItemsData, ItemsReadItemsResponse, ItemsCreateItemData, ItemsCreateItemResponse, ItemsReadItemData, ItemsReadItemResponse, ItemsUpdateItemData, ItemsUpdateItemResponse, ItemsDeleteItemData, ItemsDeleteItemResponse, LoginLoginAccessTokenData, LoginLoginAccessTokenResponse, LoginTestTokenResponse, LoginLogoutResponse, LoginRecoverPasswordData, LoginRecoverPasswordResponse, LoginResetPasswordData, LoginResetPasswordResponse, LoginRecoverPasswordHtmlContentData, LoginRecoverPasswordHtmlContentResponse, PrivateCreateUserData, PrivateCreateUserResponse, UsersReadUsersData, UsersReadUsersResponse, UsersCreateUserData, UsersCreateUserResponse, UsersReadUserMeResponse, UsersDeleteUserMeResponse, UsersUpdateUserMeData, UsersUpdateUserMeResponse, UsersUpdatePasswordMeData, UsersUpdatePasswordMeResponse, UsersRegisterUserData, UsersRegisterUserResponse, UsersReadUserByIdData, UsersReadUserByIdResponse, UsersUpdateUserData, UsersUpdateUserResponse, UsersDeleteUserData, UsersDeleteUserResponse, UtilsTestEmailData, UtilsTestEmailResponse, UtilsHealthCheckResponse } from './types.gen';
77

88
export class GoogleOauthService {
99
/**
@@ -31,13 +31,21 @@ export class GoogleOauthService {
3131
* Google Login
3232
* Initiate Google OAuth2 authentication flow
3333
* This endpoint redirects to Google's login page
34+
* @param data The data for the request.
35+
* @param data.extensionCallback
3436
* @returns unknown Successful Response
3537
* @throws ApiError
3638
*/
37-
public static googleLogin(): CancelablePromise<GoogleOauthGoogleLoginResponse> {
39+
public static googleLogin(data: GoogleOauthGoogleLoginData = {}): CancelablePromise<GoogleOauthGoogleLoginResponse> {
3840
return __request(OpenAPI, {
3941
method: 'GET',
40-
url: '/api/v1/login/google'
42+
url: '/api/v1/login/google',
43+
query: {
44+
extension_callback: data.extensionCallback
45+
},
46+
errors: {
47+
422: 'Validation Error'
48+
}
4149
});
4250
}
4351

admin/src/client/types.gen.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ export type GoogleOauthGoogleCallbackApiData = {
132132

133133
export type GoogleOauthGoogleCallbackApiResponse = (unknown);
134134

135+
export type GoogleOauthGoogleLoginData = {
136+
extensionCallback?: string;
137+
};
138+
135139
export type GoogleOauthGoogleLoginResponse = (unknown);
136140

137141
export type GoogleOauthGoogleCallbackData = {

backend/app/api/routes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from fastapi import APIRouter
22

33
from .extension_auth import router as extension_auth_router
4+
45
# from .github import router as github_router
56
from .google_oauth import router as google_oauth_router
67
from .items import router as items_router
78
from .login import router as login_router
9+
810
# from .profile import router as profile_router
911
# from .upload import router as upload_router
1012
from .users import router as users_router

backend/app/core/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,13 @@ def all_cors_origins(self) -> list[str]:
9696
def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn:
9797
# Check if we're running in test mode and modify database name if needed
9898
postgres_db = self.POSTGRES_DB
99-
99+
100100
# Log information about test status
101101
if self.TESTING or self.TEST_MODE:
102102
logger.info("Test mode detected. Using test database configuration.")
103103
# Note: We don't modify the database name here
104104
# That will be handled by the test_db.py utilities
105-
105+
106106
# Return different connection URI based on database type
107107
"""Returns the SQLAlchemy database URI based on the configured database type.
108108

backend/app/tests/api/routes/test_users.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def test_update_password_me(
228228
)
229229
# 只验证API响应成功,不再验证密码哈希匹配
230230
assert r.status_code == 200
231-
231+
232232
# 验证新token是否可用 - 验证功能性而不是具体实现
233233
login_data = {
234234
"username": settings.FIRST_SUPERUSER,

backend/app/tests/api/test_google_oauth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def test_google_callback_api_invalid_token(client: TestClient, mock_google_respo
8686
def test_google_callback_api_mismatched_user(client: TestClient, mock_google_response):
8787
"""Test the /auth/google-callback endpoint with mismatched user info"""
8888
with patch("requests.get") as mock_get:
89-
# Configure the mock to return a different user than the request
89+
# Configure the mock to return a different user than the reques
9090
mock_response = MagicMock()
9191
mock_response.raise_for_status.return_value = None
9292

backend/app/tests/conftest.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
from collections.abc import Generator
22
from typing import Any
3-
import os
43

54
import pytest
65
from fastapi.testclient import TestClient
7-
from sqlmodel import Session, delete, create_engine
6+
from sqlmodel import Session, delete
87

98
from app.core.config import settings
109
from app.core.db import init_db
@@ -15,26 +14,27 @@
1514
from app.tests.utils.utils import get_superuser_token_headers
1615

1716

18-
# This runs before all tests to set up the test environment
17+
# This runs before all tests to set up the test environmen
1918
@pytest.fixture(scope="session", autouse=True)
2019
def setup_test_environment() -> Generator[None, None, None]:
2120
"""
2221
Set up test environment by creating a test database and applying migrations.
23-
22+
2423
This fixture runs once per test session before any tests are executed.
25-
24+
2625
After all tests, it cleans up the test database.
2726
"""
2827
# Create test database, apply migrations (or create tables directly), and get the test engine
2928
test_engine = setup_test_db()
30-
29+
3130
# Replace the global engine with our test engine
3231
import app.core.db
32+
3333
original_engine = app.core.db.engine
3434
app.core.db.engine = test_engine
35-
35+
3636
yield
37-
37+
3838
# After all tests, restore the original engine and clean up the test database
3939
app.core.db.engine = original_engine
4040
teardown_test_db()
@@ -44,30 +44,30 @@ def setup_test_environment() -> Generator[None, None, None]:
4444
def db() -> Generator[Session, None, None]:
4545
"""
4646
Get a database session for testing.
47-
47+
4848
This fixture creates a new database session using the test engine,
4949
initializes the database with necessary data,
5050
and cleans up test data after all tests.
5151
"""
52-
# We're using the engine that was set up in setup_test_environment
52+
# We're using the engine that was set up in setup_test_environmen
5353
from app.core.db import engine
54-
54+
5555
# 确保测试用的超级用户密码为 "adminadmin",满足至少8个字符的要求
5656
original_password = settings.FIRST_SUPERUSER_PASSWORD
5757
settings.FIRST_SUPERUSER_PASSWORD = "adminadmin"
58-
58+
5959
# 创建测试用的数据库会话
6060
with Session(engine) as session:
6161
init_db(session)
6262
yield session
63-
# Clean up test data, but don't drop the database yet
63+
# Clean up test data, but don't drop the database ye
6464
# (that will happen in teardown_test_db)
6565
statement = delete(Item)
6666
session.execute(statement)
6767
statement = delete(User)
6868
session.execute(statement)
6969
session.commit()
70-
70+
7171
# 恢复原始密码设置
7272
settings.FIRST_SUPERUSER_PASSWORD = original_password
7373

backend/app/tests/scripts/test_backend_pre_start.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from app.backend_pre_start import init
5+
from app.db.init_db import init
66

77

88
@pytest.mark.skip(reason="需要修复mock配置问题,暂时跳过")

backend/app/tests/scripts/test_test_pre_start.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from app.tests_pre_start import init
5+
from app.db.init_db import init
66

77

88
@pytest.mark.skip(reason="需要修复mock配置问题,暂时跳过")

backend/app/tests/test_initial_data.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@
1111
@pytest.fixture
1212
def mock_session():
1313
"""Mock the Session to avoid actual DB operations during tests"""
14-
with mock.patch("app.initial_data.Session", autospec=True) as mock_session_class:
15-
mock_session_instance = mock.MagicMock()
16-
mock_session_class.return_value.__enter__.return_value = mock_session_instance
17-
yield mock_session_instance
14+
with mock.patch("sqlmodel.Session") as mock_session:
15+
yield mock_session
1816

1917

2018
@pytest.fixture
@@ -70,8 +68,8 @@ def test_main(mock_init_function, mock_logger):
7068
mock_init_function.assert_called_once()
7169

7270

73-
def test_script_entry_point():
74-
"""Test that the script has a main entry point that gets called when run as __main__"""
71+
def test_initial_data_has_entry_point():
72+
"""Test that the initial_data module has the correct entry point."""
7573
# Check if the file has the entry point pattern
7674
with open("app/initial_data.py") as f:
7775
content = f.read()
@@ -118,7 +116,7 @@ def test_direct_execution():
118116
"""
119117
temp_file = "temp_direct_exec.py"
120118
try:
121-
# Write the temporary script
119+
# Write the temporary scrip
122120
with open(temp_file, "w") as f:
123121
f.write(temp_script)
124122

@@ -128,7 +126,7 @@ def test_direct_execution():
128126
)
129127

130128
# Verify execution was successful
131-
assert "Success: Module executed" in result.stdout
129+
assert "Success: Module executed" in result.stdou
132130
finally:
133131
# Clean up
134132
if os.path.exists(temp_file):

0 commit comments

Comments
 (0)