trainingpeaks-sdk
Version:
TypeScript SDK for TrainingPeaks API integration
262 lines (261 loc) • 11.1 kB
JavaScript
import { ValidationError } from './domain/errors/index.js';
const HARDCODED_DEFAULTS = {
urls: {
baseUrl: 'https://www.trainingpeaks.com',
apiBaseUrl: 'https://tpapi.trainingpeaks.com',
loginUrl: 'https://home.trainingpeaks.com/login',
appUrl: 'https://app.trainingpeaks.com',
tokenUrl: 'https://tpapi.trainingpeaks.com/users/v3/token',
userInfoUrl: 'https://tpapi.trainingpeaks.com/users/v3/user',
},
auth: {
cookieName: 'Production_tpAuth',
},
timeouts: {
default: 30000,
webAuth: 30000,
apiAuth: 30000,
elementWait: 5000,
pageLoad: 2000,
errorDetection: 15000,
testExecution: 60000,
},
tokens: {
refreshWindow: 300000,
validationWindow: 60000,
defaultExpiration: 82800000,
},
browser: {
headless: true,
executablePath: '',
launchTimeout: 30000,
pageWaitTimeout: 2000,
},
debug: {
enabled: false,
level: 'info',
logAuth: false,
logNetwork: false,
logBrowser: false,
},
requests: {
defaultHeaders: {
'User-Agent': 'TrainingPeaks-SDK/1.0.0',
Accept: 'application/json',
'Content-Type': 'application/json',
},
retryAttempts: 3,
retryDelay: 1000,
},
};
function getEnvironmentConfig() {
return {
urls: {
baseUrl: process.env.TRAININGPEAKS_BASE_URL || undefined,
apiBaseUrl: process.env.TRAININGPEAKS_API_BASE_URL || undefined,
loginUrl: process.env.TRAININGPEAKS_LOGIN_URL || undefined,
appUrl: process.env.TRAININGPEAKS_APP_URL || undefined,
tokenUrl: process.env.TRAININGPEAKS_TOKEN_URL || undefined,
userInfoUrl: process.env.TRAININGPEAKS_USER_INFO_URL || undefined,
},
auth: {
cookieName: process.env.TRAININGPEAKS_AUTH_COOKIE_NAME || undefined,
},
timeouts: {
default: process.env.TRAININGPEAKS_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
webAuth: process.env.TRAININGPEAKS_WEB_AUTH_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_WEB_AUTH_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
apiAuth: process.env.TRAININGPEAKS_API_AUTH_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_API_AUTH_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
elementWait: process.env.TRAININGPEAKS_ELEMENT_WAIT_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_ELEMENT_WAIT_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
pageLoad: process.env.TRAININGPEAKS_PAGE_LOAD_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_PAGE_LOAD_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
errorDetection: process.env.TRAININGPEAKS_ERROR_DETECTION_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_ERROR_DETECTION_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
testExecution: process.env.TRAININGPEAKS_TEST_EXECUTION_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_TEST_EXECUTION_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
},
tokens: {
refreshWindow: process.env.TRAININGPEAKS_TOKEN_REFRESH_WINDOW
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_TOKEN_REFRESH_WINDOW, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
validationWindow: process.env.TRAININGPEAKS_TOKEN_VALIDATION_WINDOW
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_TOKEN_VALIDATION_WINDOW, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
defaultExpiration: process.env.TRAININGPEAKS_TOKEN_DEFAULT_EXPIRATION
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_TOKEN_DEFAULT_EXPIRATION, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
},
browser: {
headless: process.env.TRAININGPEAKS_BROWSER_HEADLESS !== undefined
? process.env.TRAININGPEAKS_BROWSER_HEADLESS !== 'false'
: undefined,
executablePath: process.env.TRAININGPEAKS_BROWSER_EXECUTABLE_PATH || undefined,
launchTimeout: process.env.TRAININGPEAKS_BROWSER_LAUNCH_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_BROWSER_LAUNCH_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
pageWaitTimeout: process.env.TRAININGPEAKS_BROWSER_PAGE_WAIT_TIMEOUT
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_BROWSER_PAGE_WAIT_TIMEOUT, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
},
debug: {
enabled: process.env.TRAININGPEAKS_DEBUG !== undefined
? process.env.TRAININGPEAKS_DEBUG === 'true'
: undefined,
logAuth: process.env.TRAININGPEAKS_DEBUG_AUTH !== undefined
? process.env.TRAININGPEAKS_DEBUG_AUTH === 'true'
: undefined,
logNetwork: process.env.TRAININGPEAKS_DEBUG_NETWORK !== undefined
? process.env.TRAININGPEAKS_DEBUG_NETWORK === 'true'
: undefined,
logBrowser: process.env.TRAININGPEAKS_DEBUG_BROWSER !== undefined
? process.env.TRAININGPEAKS_DEBUG_BROWSER === 'true'
: undefined,
},
requests: {
defaultHeaders: process.env.TRAININGPEAKS_DEFAULT_HEADERS
? JSON.parse(process.env.TRAININGPEAKS_DEFAULT_HEADERS)
: undefined,
retryAttempts: process.env.TRAININGPEAKS_RETRY_ATTEMPTS
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_RETRY_ATTEMPTS, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
retryDelay: process.env.TRAININGPEAKS_RETRY_DELAY
? (() => {
const parsed = parseInt(process.env.TRAININGPEAKS_RETRY_DELAY, 10);
return isNaN(parsed) ? undefined : parsed;
})()
: undefined,
},
};
}
function deepMergeConfig(base, overrides) {
const result = { ...base };
if (overrides.urls) {
result.urls = { ...result.urls, ...overrides.urls };
}
if (overrides.timeouts) {
result.timeouts = { ...result.timeouts, ...overrides.timeouts };
}
if (overrides.tokens) {
result.tokens = { ...result.tokens, ...overrides.tokens };
}
if (overrides.browser) {
result.browser = { ...result.browser, ...overrides.browser };
}
if (overrides.debug) {
result.debug = { ...result.debug, ...overrides.debug };
}
if (overrides.requests) {
result.requests = { ...result.requests, ...overrides.requests };
}
return result;
}
export function mergeWithDefaultConfig(userConfig = {}) {
const envConfig = getEnvironmentConfig();
const config = { ...HARDCODED_DEFAULTS };
if (envConfig.urls) {
const filteredUrls = Object.fromEntries(Object.entries(envConfig.urls).filter(([, value]) => value !== undefined));
config.urls = { ...config.urls, ...filteredUrls };
}
if (envConfig.auth) {
const filteredAuth = Object.fromEntries(Object.entries(envConfig.auth).filter(([, value]) => value !== undefined));
config.auth = { ...config.auth, ...filteredAuth };
}
if (envConfig.timeouts) {
const filteredTimeouts = Object.fromEntries(Object.entries(envConfig.timeouts).filter(([, value]) => value !== undefined));
config.timeouts = { ...config.timeouts, ...filteredTimeouts };
}
if (envConfig.tokens) {
const filteredTokens = Object.fromEntries(Object.entries(envConfig.tokens).filter(([, value]) => value !== undefined));
config.tokens = { ...config.tokens, ...filteredTokens };
}
if (envConfig.browser) {
const filteredBrowser = Object.fromEntries(Object.entries(envConfig.browser).filter(([, value]) => value !== undefined));
config.browser = { ...config.browser, ...filteredBrowser };
}
if (envConfig.debug) {
const filteredDebug = Object.fromEntries(Object.entries(envConfig.debug).filter(([, value]) => value !== undefined));
config.debug = { ...config.debug, ...filteredDebug };
}
if (envConfig.requests) {
const filteredRequests = Object.fromEntries(Object.entries(envConfig.requests).filter(([, value]) => value !== undefined));
config.requests = { ...config.requests, ...filteredRequests };
}
return deepMergeConfig(config, userConfig);
}
export function validateConfig(config) {
Object.entries(config.urls).forEach(([key, url]) => {
if (!url || typeof url !== 'string') {
throw new ValidationError(`Invalid URL configuration for ${key}: ${url}`);
}
try {
new URL(url);
}
catch {
throw new ValidationError(`Invalid URL format for ${key}: ${url}`);
}
});
Object.entries(config.timeouts).forEach(([key, timeout]) => {
if (typeof timeout !== 'number' || timeout < 0) {
throw new ValidationError(`Invalid timeout configuration for ${key}: ${timeout}`);
}
});
Object.entries(config.tokens).forEach(([key, value]) => {
if (typeof value !== 'number' || value < 0) {
throw new ValidationError(`Invalid token configuration for ${key}: ${value}`);
}
});
}
export function getSDKConfig(userConfig = {}) {
const config = mergeWithDefaultConfig(userConfig);
validateConfig(config);
return config;
}