UNPKG

@gsb-core/core

Version:

GSB core services and classes for platform-independent web applications

322 lines 12.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GsbApiService = void 0; const gsb_config_1 = require("../config/gsb-config"); const services_1 = require("../services"); const utils_1 = require("../utils"); // Store a singleton instance let apiServiceInstance = null; class GsbApiService { constructor() { // We'll use a simpler structure with only one base URL that changes based on tenant this.pendingBulkCalls = new Map(); this.bulkCallCounter = 0; } /** * Get the singleton instance of the service */ static getInstance() { if (!apiServiceInstance) { apiServiceInstance = new GsbApiService(); } return apiServiceInstance; } getBaseUrl(tenantCode, baseDomain) { return gsb_config_1.GSB_CONFIG.API_URL(tenantCode, baseDomain); } /** * Extract tenant code from a JWT token * @param token The JWT token * @returns The tenant code or undefined */ extractTenantCode(token) { return gsb_config_1.GSB_CONFIG.extractTenantCode(token); } /** * Generate a unique request ID for bulk operations */ generateReqId() { return `req_${Date.now()}_${++this.bulkCallCounter}`; } /** * Get the bulk batch key for grouping requests */ getBulkBatchKey(tenantCode, token) { return `${tenantCode || 'default'}_${token || 'noauth'}`; } /** * Ensure request has a unique ID for bulk operations */ ensureRequestId(request) { if (request.content && typeof request.content === 'object') { // Check if the request content doesn't have an id if (!request.content.id) { request.content.id = utils_1.GsbUtils.newId(); } } } /** * Execute a bulk batch */ async executeBulkBatch(batchKey) { var _a; const batch = this.pendingBulkCalls.get(batchKey); if (!batch || batch.calls.length === 0) { return; } // Remove the batch from pending calls this.pendingBulkCalls.delete(batchKey); try { // Prepare bulk request - use request.id as reqId for proper response matching const bulkCalls = batch.calls.map(call => { var _a; return ({ endPoint: call.endPoint, request: call.request.content, reqId: ((_a = call.request.content) === null || _a === void 0 ? void 0 : _a.id) || call.reqId // Use request.id if available, fallback to generated reqId }); }); const bulkRequest = { bulkCalls, variation: { tenantCode: batch.tenantCode || 'default', langCode: (0, gsb_config_1.getGsbLangCode)() } }; // Execute bulk request const bulkResponse = await this.httpCallAsync({ method: 'POST', protocol: 'https', hostName: 'api', content: bulkRequest, path: '/api/entity/bulk', bearerToken: batch.token, contentType: 'application/json', tenantCode: batch.tenantCode }); // Process individual responses const bulkResult = bulkResponse; if (bulkResult.results) { // Create a map for quick lookup using reqId (which should match request.id) const resultMap = new Map(); for (const result of bulkResult.results) { if (result.reqId && result.response) { try { resultMap.set(result.reqId, JSON.parse(result.response)); } catch (e) { resultMap.set(result.reqId, result.response); } } } // Resolve individual promises using the request.id to match responses for (const call of batch.calls) { const lookupId = ((_a = call.request.content) === null || _a === void 0 ? void 0 : _a.id) || call.reqId; const result = resultMap.get(lookupId); if (result !== undefined) { call.resolve(result); } else { call.reject(new Error(`No result found for request ID ${lookupId}`)); } } } else { // If no results, reject all calls for (const call of batch.calls) { call.reject(new Error('Bulk operation failed: No results returned')); } } } catch (error) { // If bulk operation fails, reject all individual calls for (const call of batch.calls) { call.reject(error); } } } /** * Add a call to the bulk batch */ addToBulkBatch(request, endPoint, token, tenantCode) { return new Promise((resolve, reject) => { var _a; // Ensure the request has a unique ID for tracking this.ensureRequestId(request); // Use the request.id as the reqId for proper response matching const reqId = ((_a = request.content) === null || _a === void 0 ? void 0 : _a.id) || this.generateReqId(); const batchKey = this.getBulkBatchKey(tenantCode, token); const pendingCall = { request, endPoint, token, tenantCode, resolve, reject, reqId }; // Get or create batch let batch = this.pendingBulkCalls.get(batchKey); if (!batch) { batch = { calls: [], timer: setTimeout(() => { this.executeBulkBatch(batchKey); }, 50), // 50ms delay tenantCode, token }; this.pendingBulkCalls.set(batchKey, batch); } else { // Clear existing timer and set a new one clearTimeout(batch.timer); batch.timer = setTimeout(() => { this.executeBulkBatch(batchKey); }, 50); } // Add call to batch batch.calls.push(pendingCall); }); } /** * Make an HTTP call */ async httpCall(dto, callback, errorCallback) { try { const response = await this.httpCallAsync(dto); if (callback) { callback(response); } return response; } catch (error) { if (errorCallback) { errorCallback(error); } throw error; } } /** * Execute HTTP call asynchronously */ async httpCallAsync(dto) { const requestData = this.convertToRequestData(dto); try { const response = await fetch(requestData.url, requestData.init); if (!response.ok) { throw response; } const data = await response.json(); return data; } catch (error) { throw error; } } /** * Convert request parameters to fetch request data */ convertToRequestData(requestParameters) { const url = new URL(requestParameters.path || '', this.getBaseUrl(requestParameters.tenantCode, requestParameters.baseDomain)).toString(); const init = { method: requestParameters.method, headers: { // Only add Authorization header if noAuth is not true and we have a bearerToken ...(!requestParameters.noAuth && requestParameters.bearerToken ? { 'Authorization': `Bearer ${requestParameters.bearerToken}` } : {}), ...requestParameters.headers } }; if (!requestParameters.content) requestParameters.content = {}; // Handle FormData requests if (requestParameters.isFormData) { const formData = new FormData(); // Convert JSON content to FormData for (const [key, value] of Object.entries(requestParameters.content)) { if (value instanceof File || value instanceof Blob) { // Handle File/Blob objects formData.append(key, value, value.name); } else if (typeof value === 'object' && value !== null) { // Handle objects by JSON stringifying them formData.append(key, JSON.stringify(value)); } else if (value !== undefined && value !== null) { // Handle primitive values formData.append(key, String(value)); } } init.body = formData; // Don't set Content-Type for FormData - let the browser set it with boundary } else { // Handle regular JSON requests init.headers = { ...init.headers, 'Content-Type': requestParameters.contentType || 'application/json' }; if (requestParameters.content) { init.body = JSON.stringify(requestParameters.content); } } return { url, init }; } /** * Call the API with the appropriate token and tenant code */ async callApi(req, endPoint, token, tenantCode) { var _a, _b, _c, _d; const authService = services_1.AuthService.getInstance(); // Skip token logic if noAuth is true let bearerToken = undefined; if (!req.noAuth) { // If no token is provided, get the appropriate token based on the endpoint if (!token) { token = authService.getToken() || undefined; } bearerToken = token; } // If no tenant code is provided and we have a token, extract it from the token if (!tenantCode && token) { tenantCode = this.extractTenantCode(token); } // If still no tenant code, get it from config if (!tenantCode) { tenantCode = authService.getTenantCode(); } if (tenantCode && req.content && !((_b = (_a = req.content) === null || _a === void 0 ? void 0 : _a.variation) === null || _b === void 0 ? void 0 : _b.tenantCode)) { if (!req.content.variation) { req.content.variation = {}; } req.content.variation.tenantCode = tenantCode; } if (tenantCode && req.content && !((_d = (_c = req.content) === null || _c === void 0 ? void 0 : _c.variation) === null || _d === void 0 ? void 0 : _d.langCode)) { if (!req.content.variation) { req.content.variation = {}; } req.content.variation.langCode = (0, gsb_config_1.getGsbLangCode)(); } // Prepare the request const request = { ...req, path: endPoint, bearerToken: bearerToken, contentType: req.contentType || 'application/json', jsonResponse: req.jsonResponse !== undefined ? req.jsonResponse : true, tenantCode: tenantCode }; // Check if we should use bulk operations const useBulk = req.useBulk !== false; if (useBulk) { // Add to bulk batch return this.addToBulkBatch(request, endPoint, bearerToken, tenantCode); } else { // Execute immediately return this.httpCall(request); } } } exports.GsbApiService = GsbApiService; //# sourceMappingURL=gsb-api.service.js.map