trainingpeaks-sdk
Version:
TypeScript SDK for TrainingPeaks API integration
96 lines (95 loc) • 4.44 kB
JavaScript
import { throwCookieNotFoundError, throwHttpErrorFromResponse, throwMissingDataError, throwTokenExpiredError, throwValidationError, } from '../../adapters/errors/http-errors.js';
import { mapTPTokenToAuthToken, mapTPUserToUser } from '../../adapters/mappers/index.js';
import { isTokenExpired, validateCredentials, } from '../../domain/index.js';
import { API_ENDPOINTS } from './constants/api-urls.js';
import { getAuthToken, getLoginPage, getRequestVerificationToken, getUser, submitLogin, } from './endpoints/index.js';
const createErrorContext = (url, method, requestData) => ({
url,
method,
...(requestData && { requestData }),
});
const createLogin = (deps) => {
return async (credentials) => {
if (!validateCredentials(credentials)) {
throwValidationError('Invalid credentials provided', createErrorContext(API_ENDPOINTS.LOGIN_PAGE, 'POST', {
username: credentials.username,
}));
}
const response = await getLoginPage(deps.httpClient);
if (!response.success) {
throwHttpErrorFromResponse(response, 'Get request verification token', createErrorContext(API_ENDPOINTS.LOGIN_PAGE, 'GET'));
}
if (!response.data) {
throwMissingDataError('No response data received from login page', createErrorContext(API_ENDPOINTS.LOGIN_PAGE, 'GET'));
}
const requestVerificationToken = getRequestVerificationToken(response.data);
if (!requestVerificationToken) {
throwValidationError('Request verification token not found', createErrorContext(API_ENDPOINTS.LOGIN_PAGE, 'GET'));
}
const submitLoginResponse = await submitLogin(deps.httpClient, requestVerificationToken, credentials);
if (!submitLoginResponse.success) {
throwHttpErrorFromResponse(submitLoginResponse, 'Submit login', createErrorContext(API_ENDPOINTS.LOGIN_PAGE, 'POST', {
username: credentials.username,
}));
}
const tpAuthCookie = submitLoginResponse.cookies?.find((cookie) => cookie.includes('Production_tpAuth'));
if (!tpAuthCookie) {
throwCookieNotFoundError('Production_tpAuth', createErrorContext(API_ENDPOINTS.LOGIN_PAGE, 'POST', {
username: credentials.username,
}));
}
const authTokenResponse = await getAuthToken(deps.httpClient, submitLoginResponse.cookies || []);
if (!authTokenResponse.success) {
throwHttpErrorFromResponse(authTokenResponse, 'Get auth token', createErrorContext(API_ENDPOINTS.TOKEN, 'GET'));
}
if (!authTokenResponse.data) {
throwMissingDataError('No auth token data received', createErrorContext(API_ENDPOINTS.TOKEN, 'GET'));
}
const authTokenData = authTokenResponse.data;
const authToken = authTokenData.token;
const userResponse = await getUser(deps.httpClient, {
accessToken: authToken.access_token,
tokenType: authToken.token_type,
expiresIn: authToken.expires_in,
expires: new Date(authToken.expires),
refreshToken: authToken.refresh_token,
scope: authToken.scope,
});
if (!userResponse.success) {
throwHttpErrorFromResponse(userResponse, 'Get user information', createErrorContext(API_ENDPOINTS.USER_PROFILE, 'GET'));
}
if (!userResponse.data) {
throwMissingDataError('No user data received', createErrorContext(API_ENDPOINTS.USER_PROFILE, 'GET'));
}
const userData = userResponse.data;
const user = mapTPUserToUser(userData.user);
const token = mapTPTokenToAuthToken(authToken);
if (isTokenExpired(token)) {
throwTokenExpiredError(createErrorContext(API_ENDPOINTS.TOKEN, 'GET'));
}
const session = {
token,
user,
};
await deps.sessionStorage.set(session);
return session;
};
};
const createLogout = (deps) => {
return async () => {
try {
await deps.sessionStorage.clear();
}
catch (error) {
deps.logger.warn('Failed to clear session storage during logout', {
error,
});
}
};
};
export const createAuthRepository = (deps) => {
return {
login: createLogin(deps),
logout: createLogout(deps),
};
};