@eka-care/patient-ts-sdk
Version:
TypeScript SDK for Trinity Patient Profile Management System
159 lines (158 loc) • 5.02 kB
JavaScript
/**
* Core HTTP client for Trinity Profiles SDK
*/
import { NetworkError, TimeoutError, TrinitySDKError, createErrorFromResponse } from './errors';
/**
* Core HTTP client class
*/
export class HttpClient {
constructor(config) {
this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
this.accessToken = config.accessToken || '';
this.timeout = config.timeout || 30000; // 30 seconds default
this.config = config;
}
/**
* Get current configuration (for token updates)
*/
getConfig() {
return {
...this.config,
baseUrl: this.baseUrl,
accessToken: this.accessToken,
timeout: this.timeout
};
}
/**
* Make HTTP request
*/
async request(options) {
const url = this.buildUrl(options.path, options.params);
const headers = this.buildHeaders(options.headers);
headers["client-id"] = "pt-directory-sdk";
const requestInit = {
method: options.method,
headers,
signal: this.createAbortSignal(),
};
// Add body for POST/PATCH requests
if (options.body && (options.method === 'POST' || options.method === 'PATCH')) {
requestInit.body = JSON.stringify(options.body);
}
try {
const response = await fetch(url, requestInit);
// Parse response
let data;
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
data = await response.json();
}
else {
data = await response.text();
}
// Handle error responses
if (!response.ok) {
const errorMessage = this.extractErrorMessage(data);
throw createErrorFromResponse(response.status, errorMessage, data);
}
// Convert headers to plain object
const responseHeaders = {};
response.headers.forEach((value, key) => {
responseHeaders[key] = value;
});
return {
data,
status: response.status,
statusText: response.statusText,
headers: responseHeaders,
};
}
catch (error) {
if (error instanceof TrinitySDKError) {
throw error;
}
if (error instanceof TypeError && error.message.includes('fetch')) {
throw new NetworkError('Network connection failed');
}
if (error instanceof Error && error.name === 'AbortError') {
throw new TimeoutError('Request timeout');
}
throw new TrinitySDKError(error instanceof Error ? error.message : 'Unknown error occurred');
}
}
/**
* GET request
*/
async get(path, params) {
return this.request({ method: 'GET', path, params });
}
/**
* POST request
*/
async post(path, body) {
return this.request({ method: 'POST', path, body });
}
/**
* PATCH request
*/
async patch(path, body) {
return this.request({ method: 'PATCH', path, body });
}
/**
* DELETE request
*/
async delete(path) {
return this.request({ method: 'DELETE', path });
}
/**
* Build complete URL with query parameters
*/
buildUrl(path, params) {
const cleanPath = path.startsWith('/') ? path : `/${path}`;
let url = `${this.baseUrl}${cleanPath}`;
if (params && Object.keys(params).length > 0) {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
searchParams.append(key, String(value));
});
url += `?${searchParams.toString()}`;
}
return url;
}
/**
* Build request headers
*/
buildHeaders(customHeaders) {
const headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
if (this.accessToken) {
headers['Authorization'] = `Bearer ${this.accessToken}`;
}
if (customHeaders) {
Object.assign(headers, customHeaders);
}
return headers;
}
/**
* Create abort signal for timeout
*/
createAbortSignal() {
const controller = new AbortController();
setTimeout(() => controller.abort(), this.timeout);
return controller.signal;
}
/**
* Extract error message from response
*/
extractErrorMessage(data) {
if (typeof data === 'string') {
return data;
}
if (data && typeof data === 'object') {
return data.error || data.message || data.detail || 'API request failed';
}
return 'API request failed';
}
}