Skip to main content
Glama
client.js3.69 kB
const fetch = require('node-fetch'); const { handleApiError, withRetry, ValidationError } = require('../utils/errors'); const { logger } = require('../utils/logger'); class StockSparkClient { constructor(authManager) { this.auth = authManager; // Default API URL for StockSpark this.apiUrl = process.env.STOCKSPARK_API_URL || 'https://carspark-api.dealerk.com'; this.validateEnvironment(); } validateEnvironment() { // No required environment variables here anymore // Username and password are validated in AuthManager // API URL has a default value } async request(path, options = {}) { const method = options.method || 'GET'; // Allow country override in options, fallback to env, default to 'it' const country = options.country || process.env.STOCKSPARK_COUNTRY || 'it'; const url = `${this.apiUrl}/${country}${path}`; try { return await withRetry(async () => { const token = await this.auth.getToken(); logger.apiRequest(method, url, options.body ? JSON.parse(options.body) : null); const response = await fetch(url, { ...options, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', ...options.headers } }); logger.apiResponse(response.status, response.statusText, url); // Handle different response statuses if (!response.ok) { let errorBody; try { const bodyText = await response.text(); try { errorBody = JSON.parse(bodyText); } catch (e) { errorBody = bodyText; } } catch (e) { errorBody = 'Unable to read response body'; } const error = new Error(`API Error: ${response.status} ${response.statusText}`); error.response = { status: response.status, statusText: response.statusText }; error.body = errorBody; throw error; } // Handle empty responses const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { return response.text(); } return response.json(); }, 3, 1000); } catch (error) { handleApiError(error, { operation: `${method} ${path}`, resource: this.extractResourceFromPath(path), url }); } } extractResourceFromPath(path) { if (path.includes('/vehicle/')) { const match = path.match(/\/vehicle\/(\d+)/); return match ? `Vehicle ${match[1]}` : 'Vehicle'; } if (path.includes('/publications/')) { return 'Publication'; } if (path.includes('/images/')) { return 'Image'; } return 'API Resource'; } // Convenience methods for common HTTP verbs async get(path, params = {}) { const queryString = new URLSearchParams(params).toString(); const fullPath = queryString ? `${path}?${queryString}` : path; return this.request(fullPath, { method: 'GET' }); } async post(path, data) { return this.request(path, { method: 'POST', body: JSON.stringify(data) }); } async put(path, data) { return this.request(path, { method: 'PUT', body: JSON.stringify(data) }); } async patch(path, data) { return this.request(path, { method: 'PATCH', body: JSON.stringify(data) }); } async delete(path) { return this.request(path, { method: 'DELETE' }); } } module.exports = { StockSparkClient };

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/loukach/stockspark-mcp-poc'

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