@dbs-portal/tool-mock
Version:
API mocking toolkit using MSW for DBS Portal development workflows
303 lines • 10.4 kB
JavaScript
/**
* API client integration for seamless mocking
*/
import { setupMocks, getMockConfig, isMswSetup } from '../core/setup';
import { getEnvironmentInfo } from '../core/environment';
/**
* Create a mock-aware API client
*/
export async function createMockAwareApiClient(config) {
const { baseURL, mockBaseURL = baseURL, enableMocking = false, mockConfig, mockHandlers = [], timeout = 30000, headers = {}, interceptors, } = config;
// Setup MSW if mocking is enabled and not already setup
if (enableMocking && !isMswSetup()) {
await setupMocks({
config: mockConfig || {},
handlers: mockHandlers,
});
}
// Create the API client implementation
const client = {
async get(url, requestConfig) {
return makeRequest('GET', url, undefined, requestConfig);
},
async post(url, data, requestConfig) {
return makeRequest('POST', url, data, requestConfig);
},
async put(url, data, requestConfig) {
return makeRequest('PUT', url, data, requestConfig);
},
async patch(url, data, requestConfig) {
return makeRequest('PATCH', url, data, requestConfig);
},
async delete(url, requestConfig) {
return makeRequest('DELETE', url, undefined, requestConfig);
},
isMocking() {
return enableMocking && isMswSetup();
},
getConfig() {
return config;
},
updateConfig(newConfig) {
Object.assign(config, newConfig);
},
};
// Internal request function
async function makeRequest(method, url, data, requestConfig) {
const fullUrl = url.startsWith('http') ? url : `${client.isMocking() ? mockBaseURL : baseURL}${url}`;
const requestHeaders = {
'Content-Type': 'application/json',
...headers,
...requestConfig?.headers,
};
// Apply request interceptors
let finalConfig = {
method,
url: fullUrl,
data,
headers: requestHeaders,
timeout,
...requestConfig,
};
if (interceptors?.request) {
for (const interceptor of interceptors.request) {
finalConfig = interceptor(finalConfig);
}
}
try {
// Make the actual request
const response = await fetch(finalConfig.url, {
method: finalConfig.method,
headers: finalConfig.headers,
body: finalConfig.data ? JSON.stringify(finalConfig.data) : null,
signal: requestConfig?.signal,
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
let responseData = await response.json();
// Apply response interceptors
if (interceptors?.response) {
for (const interceptor of interceptors.response) {
responseData = interceptor(responseData);
}
}
// Handle DBS Portal API response format
if (responseData.success !== undefined) {
if (responseData.success) {
return responseData.data;
}
else {
throw new Error(responseData.error?.message || 'API request failed');
}
}
return responseData;
}
catch (error) {
// Apply error interceptors
if (interceptors?.response) {
for (const interceptor of interceptors.response) {
try {
return interceptor({ error });
}
catch {
// Continue with original error
}
}
}
throw error;
}
}
return client;
}
/**
* Create environment-aware API client
*/
export async function createEnvironmentAwareApiClient(config) {
const envInfo = getEnvironmentInfo();
let environmentConfig;
if (envInfo.runtime.isProduction) {
environmentConfig = config.production;
}
else if (envInfo.runtime.isTest) {
environmentConfig = config.testing;
}
else if (config.staging && process.env['NODE_ENV'] === 'staging') {
environmentConfig = config.staging;
}
else {
environmentConfig = config.development;
}
// Merge with base configuration
const finalConfig = {
baseURL: '',
enableMocking: envInfo.shouldMock,
...environmentConfig,
};
return createMockAwareApiClient(finalConfig);
}
/**
* Wrap existing HTTP client with mock capabilities
*/
export function wrapHttpClientWithMocks(wrapperConfig) {
const { client, requestTransformer, responseTransformer, errorTransformer } = wrapperConfig;
// Store original methods
const originalRequest = client.request?.bind(client);
const originalGet = client.get?.bind(client);
const originalPost = client.post?.bind(client);
const originalPut = client.put?.bind(client);
const originalPatch = client.patch?.bind(client);
const originalDelete = client.delete?.bind(client);
// Create wrapper functions
const wrapMethod = (originalMethod, method) => {
return async (...args) => {
try {
// Apply request transformer
if (requestTransformer) {
args = requestTransformer({ method, args });
}
const result = await originalMethod(...args);
// Apply response transformer
if (responseTransformer) {
return responseTransformer(result);
}
return result;
}
catch (error) {
// Apply error transformer
if (errorTransformer) {
throw errorTransformer(error);
}
throw error;
}
};
};
// Wrap methods if they exist
if (originalRequest) {
client.request = wrapMethod(originalRequest, 'request');
}
if (originalGet) {
client.get = wrapMethod(originalGet, 'GET');
}
if (originalPost) {
client.post = wrapMethod(originalPost, 'POST');
}
if (originalPut) {
client.put = wrapMethod(originalPut, 'PUT');
}
if (originalPatch) {
client.patch = wrapMethod(originalPatch, 'PATCH');
}
if (originalDelete) {
client.delete = wrapMethod(originalDelete, 'DELETE');
}
// Add mock-specific methods
client.isMocking = () => isMswSetup();
client.getMockConfig = () => getMockConfig();
return client;
}
/**
* Create API client factory
*/
export function createApiClientFactory(defaultConfig = {}) {
return {
/**
* Create client for specific service
*/
createServiceClient: async (serviceName, serviceConfig = {}) => {
const config = {
...defaultConfig,
...serviceConfig,
baseURL: serviceConfig.baseURL || `${defaultConfig.baseURL}/${serviceName}`,
};
return createMockAwareApiClient(config);
},
/**
* Create client with authentication
*/
createAuthenticatedClient: async (token, clientConfig = {}) => {
const config = {
...defaultConfig,
...clientConfig,
headers: {
...defaultConfig.headers,
...clientConfig.headers,
Authorization: `Bearer ${token}`,
},
};
return createMockAwareApiClient(config);
},
/**
* Create client with custom interceptors
*/
createInterceptedClient: async (interceptors, clientConfig = {}) => {
const config = {
...defaultConfig,
...clientConfig,
interceptors: {
...defaultConfig.interceptors,
...interceptors,
},
};
return createMockAwareApiClient(config);
},
};
}
/**
* Integration with existing @dbs-portal/core-api HttpClient
*/
export async function integrateWithCoreApiClient(HttpClientClass, mockConfig) {
// Create a new class that extends the original HttpClient
class MockAwareHttpClient extends HttpClientClass {
mockClient;
constructor(config) {
super(config);
// Initialize mock client if mocking is enabled
if (mockConfig?.enableMocking) {
this.initializeMockClient(config, mockConfig);
}
}
async initializeMockClient(config, mockConf) {
this.mockClient = await createMockAwareApiClient({
baseURL: config.baseURL || '',
...mockConf,
});
}
// Override HTTP methods to use mock client when available
async get(...args) {
if (this.mockClient?.isMocking()) {
return this.mockClient.get(...args);
}
return super.get(...args);
}
async post(...args) {
if (this.mockClient?.isMocking()) {
return this.mockClient.post(...args);
}
return super.post(...args);
}
async put(...args) {
if (this.mockClient?.isMocking()) {
return this.mockClient.put(...args);
}
return super.put(...args);
}
async patch(...args) {
if (this.mockClient?.isMocking()) {
return this.mockClient.patch(...args);
}
return super.patch(...args);
}
async delete(...args) {
if (this.mockClient?.isMocking()) {
return this.mockClient.delete(...args);
}
return super.delete(...args);
}
isMocking() {
return this.mockClient?.isMocking() || false;
}
}
return MockAwareHttpClient;
}
//# sourceMappingURL=api-client.js.map