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:
senke 2026-04-25 23:25:43 +02:00
parent feb5fc02be
commit c488a4b8d6
2 changed files with 70 additions and 8 deletions

View file

@ -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();

View file

@ -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
* queryheader 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' });
}