Skip to main content
Glama
apiClient.ts8.26 kB
import axios, { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'; import logger from '../../utils/logger.js'; import config from '../../utils/config.js'; // Debug environment variables logger.info(`TEAMWORK_DOMAIN: ${config.domain}`); logger.info(`Constructed API URL: ${config.apiUrl}`); logger.info(`TEAMWORK_USERNAME: ${config.username}`); logger.info(`TEAMWORK_PASSWORD length: ${config.password ? config.password.length : 0}`); // Create auth header with debugging const username = config.username; const password = config.password; const authString = `${username}:${password}`; const base64Auth = Buffer.from(authString).toString('base64'); logger.info(`Auth string length: ${authString.length}, Base64 auth length: ${base64Auth.length}`); // Configure Teamwork API clients let teamworkApiV3: AxiosInstance | null = null; let teamworkApiV1: AxiosInstance | null = null; /** * Constructs the Teamwork API URL for a specific version * @param version API version (v1, v3, etc.) * @returns The API URL for the specified version */ export const getApiUrlForVersion = (version: string = 'v3'): string => { if (!config.domain) { logger.error('Teamwork domain is not set'); return ''; } // Remove any http/https prefix if present const cleanDomain = config.domain.replace(/^(https?:\/\/)/, ''); // Remove .teamwork.com if present (in case user enters full domain) const baseDomain = cleanDomain.replace(/\.teamwork\.com$/, ''); // Remove any trailing slashes const trimmedDomain = baseDomain.replace(/\/+$/, ''); // For v1 API, the URL format is different if (version === 'v1') { return `https://${trimmedDomain}.teamwork.com/`; } // For v3 and other versions return `https://${trimmedDomain}.teamwork.com/projects/api/${version}/`; }; /** * Creates an API client for a specific Teamwork API version * @param version API version (v1, v3, etc.) * @returns Configured Axios instance for the specified API version */ export const createApiClientForVersion = (version: string = 'v3'): AxiosInstance | null => { try { const apiUrl = getApiUrlForVersion(version); if (!apiUrl) { throw new Error(`Invalid or empty Teamwork API URL for version ${version}`); } logger.info(`Creating API client for version ${version} with baseURL: ${apiUrl}`); const api = axios.create({ baseURL: apiUrl, headers: { 'Authorization': `Basic ${base64Auth}`, 'Content-Type': 'application/json' } }); // Log request interceptor for debugging api.interceptors.request.use((request: InternalAxiosRequestConfig) => { // Remove any prefix / from the url const url = request.url?.replace(/^\/+/, ''); logger.info(`Request URL: ${request.baseURL}${url}`); logger.info(`Request method: ${request.method?.toUpperCase()}`); // Don't log full headers to avoid exposing auth credentials in logs const safeHeaders = { ...request.headers }; if (safeHeaders.Authorization) { safeHeaders.Authorization = 'Basic ***** (redacted)'; } logger.info(`Request headers: ${JSON.stringify(safeHeaders)}`); // Log request body for POST/PUT/PATCH requests if (request.data && ['post', 'put', 'patch'].includes(request.method || '')) { logger.info(`Request body: ${JSON.stringify(request.data)}`); } // Log query parameters if present if (request.params) { logger.info(`Request params: ${JSON.stringify(request.params)}`); } return request; }, (error: AxiosError) => { logger.error(`Request setup error: ${error.message}`); if (error.stack) { logger.error(`Stack trace: ${error.stack}`); } return Promise.reject(error); }); // Log response interceptor for debugging api.interceptors.response.use((response: AxiosResponse) => { logger.info(`Response status: ${response.status} ${response.statusText}`); // Log response headers logger.verbose(`Response headers: ${JSON.stringify(response.headers)}`); // Log response data preview const dataType = typeof response.data; if (dataType === 'object' && response.data !== null) { logger.info(`4️⃣`); if (Array.isArray(response.data)) { logger.info(`5️⃣`); logger.info(`Response data: Array with ${response.data.length} items`); if (response.data.length > 0) { logger.info(`6️⃣`); logger.info(`First item sample: ${JSON.stringify(response.data[0]).substring(0, 200)}...`); } } else { logger.info(`7️⃣`); const keys = Object.keys(response.data); logger.info(`Response data: Object with keys [${keys.join(', ')}]`); logger.info(`Data preview: ${JSON.stringify(response.data).substring(0, 200)}...`); } } else { logger.info(`Response data type: ${dataType}`); } return response; }, (error: AxiosError) => { logger.info(`8️⃣`); if (error.response) { logger.error(`Response error: ${error.response.status} - ${error.response.statusText}`); logger.error(`Response headers: ${JSON.stringify(error.response.headers)}`); // Log response data if available if (error.response.data) { logger.error(`Response data: ${JSON.stringify(error.response.data)}`); } } else if (error.request) { logger.error(`Request error (no response received): ${error.message}`); logger.error(`Request details: ${JSON.stringify(error.request)}`); } else { logger.error(`Error setting up request: ${error.message}`); } if (error.config) { logger.error(`Request config: ${JSON.stringify({ url: error.config.url, method: error.config.method, baseURL: error.config.baseURL, timeout: error.config.timeout })}`); } if (error.stack) { logger.error(`Stack trace: ${error.stack}`); } return Promise.reject(error); }); return api; } catch (error: any) { logger.error(`Failed to create Teamwork API client for version ${version}: ${error.message}`); return null; } }; // Helper function to create and configure the default API client (v3) export const createApiClient = (): AxiosInstance | null => { return createApiClientForVersion('v3'); }; // Initialize the default API client (v3) teamworkApiV3 = createApiClient(); // Helper function to check if default API client (v3) is initialized export const ensureApiClient = (): AxiosInstance => { if (!teamworkApiV3) { teamworkApiV3 = createApiClient(); if (!teamworkApiV3) { const errorMsg = 'Teamwork API client (v3) is not initialized. Please check your configuration.'; logger.error(errorMsg); throw new Error(errorMsg); } } return teamworkApiV3; }; /** * Gets or creates an API client for a specific version * @param version API version (v1, v3, etc.) * @returns Axios instance for the specified API version */ export const getApiClientForVersion = (version: string = 'v3'): AxiosInstance => { // For v3 API (default), use the existing client if (version === 'v3') { return ensureApiClient(); } // For v1 API if (version === 'v1') { if (!teamworkApiV1) { teamworkApiV1 = createApiClientForVersion('v1'); if (!teamworkApiV1) { const errorMsg = 'Teamwork API client (v1) could not be initialized. Please check your configuration.'; logger.error(errorMsg); throw new Error(errorMsg); } } return teamworkApiV1; } // For any other version, create a new client each time const apiClient = createApiClientForVersion(version); if (!apiClient) { const errorMsg = `Teamwork API client (${version}) could not be initialized. Please check your configuration.`; logger.error(errorMsg); throw new Error(errorMsg); } return apiClient; }; export default { createApiClient, ensureApiClient, createApiClientForVersion, getApiClientForVersion };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Vizioz/Teamwork-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server