@gsb-core/core
Version:
GSB core services and classes for platform-independent web applications
322 lines • 12.2 kB
JavaScript
;
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