refactor(web): migrate authService partial to orval (v1.0.8 B6)
Fourth feature-service migration after dashboard / profile / playlist /
track. Replaces 4 of 9 raw apiClient calls in
@/features/auth/services/authService.ts with orval-generated functions
from services/generated/auth/auth.ts.
Functions migrated (4):
- login → postAuthLogin
- logout → postAuthLogout (empty body)
- resendVerificationEmail → postAuthResendVerification
- checkUsernameAvailability → getAuthCheckUsername
Functions deliberately NOT migrated (5, deferred v1.0.9 — would need
backend annotation fixes or careful prod verification before changing
the wire shape on critical auth paths):
- register — backend DTO `register_request.go:8` declares
`json:"password_confirmation"` but the frontend
currently sends `password_confirm`. orval-typed body
would force the rename, which is the correct shape
per the swaggo spec but a behaviour change on a
critical path. Needs a separate validation pass
against staging before flipping.
- refreshToken — same drift: backend DTO uses `refresh_token`,
frontend uses `refreshToken`. Identical risk profile.
- requestPasswordReset / resetPassword — endpoints not yet annotated
in swaggo (no /auth/password/* paths in
openapi.yaml). Backend annotation extension required
first.
- verifyEmail — verb drift (frontend GET /auth/verify-email?token=
vs orval-generated POST). Coupled with the parked
FUNCTIONAL_AUDIT §4#7 query→header migration; both
should land together.
Test rewrite: orval module mocked to delegate back to the existing
apiClient mock. The 17 existing assertions on
`expect(apiClient.post).toHaveBeenCalledWith('/auth/...', ...)` keep
working without rewriting the test bodies, same shim pattern as B4 / B5.
Tests: 302/302 in features/auth/ green. npm run typecheck: clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
feb5fc02be
commit
c488a4b8d6
2 changed files with 70 additions and 8 deletions
|
|
@ -33,6 +33,32 @@ const mockedApiClient = apiClient as {
|
|||
get: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
// v1.0.8 B6 — login / logout / resendVerificationEmail /
|
||||
// checkUsernameAvailability migrated to orval. Tests still mock
|
||||
// `apiClient.post/get` for legacy ergonomics; the orval module's
|
||||
// functions are stubbed to delegate back to those mocks so existing
|
||||
// `toHaveBeenCalledWith('/auth/...', ...)` assertions keep working.
|
||||
vi.mock('@/services/generated/auth/auth', () => ({
|
||||
postAuthLogin: vi.fn(async (body: unknown) =>
|
||||
mockedApiClient.post('/auth/login', body).then((r: { data?: unknown }) => r?.data),
|
||||
),
|
||||
postAuthLogout: vi.fn(async () =>
|
||||
mockedApiClient.post('/auth/logout').then((r: { data?: unknown }) => r?.data),
|
||||
),
|
||||
postAuthResendVerification: vi.fn(async (body: unknown) =>
|
||||
mockedApiClient
|
||||
.post('/auth/resend-verification', body)
|
||||
.then((r: { data?: unknown }) => r?.data),
|
||||
),
|
||||
getAuthCheckUsername: vi.fn(async (params: { username: string }) =>
|
||||
mockedApiClient
|
||||
.get(
|
||||
`/auth/check-username?username=${encodeURIComponent(params.username)}`,
|
||||
)
|
||||
.then((r: { data?: unknown }) => r?.data),
|
||||
),
|
||||
}));
|
||||
|
||||
describe('authService', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,32 @@
|
|||
/**
|
||||
* Auth service — orval-backed (partial v1.0.8 B6).
|
||||
*
|
||||
* Migrated to orval generated client:
|
||||
* login, logout, resendVerificationEmail, checkUsernameAvailability.
|
||||
*
|
||||
* Still on raw apiClient (deferred v1.0.9 — backend annotation gaps or
|
||||
* field-name drift would risk breaking auth):
|
||||
* - register → backend DTO says `password_confirmation` while the
|
||||
* frontend currently sends `password_confirm`
|
||||
* (register_request.go:8). Renaming via orval would
|
||||
* change wire shape; needs explicit verification on
|
||||
* prod first.
|
||||
* - refreshToken → same pattern, backend expects `refresh_token` but
|
||||
* current frontend sends `refreshToken`.
|
||||
* - requestPasswordReset / resetPassword → endpoints not yet annotated
|
||||
* in swaggo (no /auth/password/* in openapi.yaml).
|
||||
* - verifyEmail → frontend uses GET /auth/verify-email?token= but
|
||||
* orval-generated spec says POST. Verb drift + the
|
||||
* query→header migration parked in FUNCTIONAL_AUDIT
|
||||
* §4#7 should land together in v1.0.9.
|
||||
*/
|
||||
import { apiClient } from '@/services/api/client';
|
||||
import {
|
||||
postAuthLogin,
|
||||
postAuthLogout,
|
||||
postAuthResendVerification,
|
||||
getAuthCheckUsername,
|
||||
} from '@/services/generated/auth/auth';
|
||||
import { handleApiServiceError } from '@/utils/serviceErrorHandler';
|
||||
import type {
|
||||
LoginFormData,
|
||||
|
|
@ -32,8 +60,10 @@ export interface AuthResponse {
|
|||
// INT-API-003: Standardized error handling using handleApiServiceError
|
||||
export async function login(data: LoginFormData): Promise<AuthResponse> {
|
||||
try {
|
||||
const response = await apiClient.post<AuthResponse>('/auth/login', data);
|
||||
return response.data;
|
||||
const response = await postAuthLogin(
|
||||
data as Parameters<typeof postAuthLogin>[0],
|
||||
);
|
||||
return response as unknown as AuthResponse;
|
||||
} catch (error) {
|
||||
handleApiServiceError(error, {
|
||||
context: 'auth',
|
||||
|
|
@ -79,7 +109,9 @@ export async function register(data: RegisterFormData): Promise<AuthResponse> {
|
|||
// INT-API-003: Standardized error handling using handleApiServiceError
|
||||
export async function logout(): Promise<void> {
|
||||
try {
|
||||
await apiClient.post('/auth/logout');
|
||||
// PostAuthLogoutBody.refresh_token is optional; the cookie-based session
|
||||
// tear-down doesn't need a body.
|
||||
await postAuthLogout({});
|
||||
} catch (error) {
|
||||
handleApiServiceError(error, { context: 'auth' });
|
||||
}
|
||||
|
|
@ -162,7 +194,7 @@ export async function verifyEmail(token: string): Promise<void> {
|
|||
// INT-API-003: Standardized error handling using handleApiServiceError
|
||||
export async function resendVerificationEmail(email: string): Promise<void> {
|
||||
try {
|
||||
await apiClient.post('/auth/resend-verification', { email });
|
||||
await postAuthResendVerification({ email });
|
||||
} catch (error) {
|
||||
handleApiServiceError(error, { context: 'auth' });
|
||||
}
|
||||
|
|
@ -179,10 +211,14 @@ export async function checkUsernameAvailability(
|
|||
username: string,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await apiClient.get<{ available: boolean }>(
|
||||
`/auth/check-username?username=${encodeURIComponent(username)}`,
|
||||
);
|
||||
return response.data.available;
|
||||
const response = await getAuthCheckUsername({ username });
|
||||
const payload = response as unknown as
|
||||
| { available: boolean }
|
||||
| { data?: { available?: boolean } };
|
||||
if ('available' in payload) {
|
||||
return payload.available;
|
||||
}
|
||||
return payload.data?.available ?? false;
|
||||
} catch (error) {
|
||||
handleApiServiceError(error, { context: 'auth' });
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue