import { ILoader } from '../../types'; import getLogger from '../../util/logger'; import OTTConfiguration from './config'; import RequestBuilder from '../../util/request-builder'; import ServiceResult from '../common/base-service-result'; import Error from '../../util/error/error'; /** * RequestSequenceBuilder provides a builder pattern for executing multiple individual requests * sequentially, as an alternative to the MultiRequestBuilder which batches them together. * @class RequestSequenceBuilder */ export default class RequestSequenceBuilder { private _logger = getLogger('RequestSequenceBuilder'); /** * Loaders to be executed * @type {Map} * @private */ public loaders: Map = new Map(); /** * Global parameters for all requests * @type {Record} * @private */ private _globalParams: Record = { apiVersion: OTTConfiguration.get().serviceParams?.apiVersion }; /** * Add a loader to the builder * @function add * @param {ILoader} loader The loader to add */ public add(loader: ILoader): void { this.loaders.set(loader.requests?.[0]?.service, loader); } /** * Executes a single request * @function execute * @param {RequestBuilder} request - The request to execute * @returns {Promise} - Promise with the service result */ private async _executeRequest( request: RequestBuilder ): Promise { request.params = JSON.stringify({ ...this._globalParams, ...request.params }); try { const data = await request.doHttpRequest(); const serviceResult = new ServiceResult(data.result || data); if (serviceResult.hasError) { const { code, message } = serviceResult.error; this._logger.error( `Service returned an error with error code: ${code} and message: ${message}.` ); } return serviceResult; } catch (error) { throw new Error( Error.Severity.CRITICAL, Error.Category.NETWORK, Error.Code.API_RESPONSE_MISMATCH, { error, request } ); } } /** * Executes multiple requests sequentially * @function executeSequence * @param {Array} requests - The requests to execute in order * @returns {Promise} - Promise with the combined results */ private async _executeSequence( requests: Array ): Promise { const results: Array = []; for (const request of requests) { try { const serviceResult = await this._executeRequest(request); results.push(serviceResult); } catch (error) { this._logger.error(`Request execution failed: ${error.message}`); } } // Create result object and check for success const singleRequestsResult = new SingleRequestsResult(results); if (singleRequestsResult.success) { return singleRequestsResult; } else { const errorResult = results.find(result => result.hasError); if (errorResult) { const { message, code } = errorResult.error; throw new Error( Error.Severity.CRITICAL, Error.Category.NETWORK, Error.Code.API_RESPONSE_MISMATCH, { error: errorResult.error, errorMessage: `Request returned an error: ${message} (code: ${code})` } ); } return singleRequestsResult; } } /** * Executes all added loaders as separate requests * @function execute * @returns {Promise>} - Promise with the loaded loaders */ public async execute(): Promise> { const sessionLoader = this.loaders.get('ottuser'); const assetLoader = this.loaders.get('asset'); let ks = ''; try { if (sessionLoader) { const serviceResult = await this._executeRequest(sessionLoader.requests[0]); sessionLoader.response = [{ data: serviceResult.data }]; ks = serviceResult.data.ks || ''; } if (assetLoader) { assetLoader.requests.forEach((request: RequestBuilder) => { if (request.params.ks === '{1:result:ks}') { request.params.ks = ks; } }); const requestsResult = await this._executeSequence(assetLoader.requests); assetLoader.response = requestsResult.results; } } catch (error) { this._logger.error(`loaders execution failed: ${error.message}`); } return this.loaders; } } export class SingleRequestsResult { private static _logger = getLogger('SingleRequestsResult'); public success: boolean; public results: Array; constructor(results: Array, requestsMustSucceed = true) { this.results = [...results]; const errorResults = this.results.filter(({ hasError }) => hasError); for (const serviceResult of errorResults) { const { code, message } = serviceResult.error; SingleRequestsResult._logger.error( `Service returned an error with error code: ${code} and message: ${message}.` ); } const allRequestsFailed = errorResults.length === this.results.length; const someRequestsFailed = requestsMustSucceed && errorResults.length > 0; if (allRequestsFailed || someRequestsFailed) { this.success = false; } else { if (!requestsMustSucceed) { this.results = this.results.filter(serviceResult => !serviceResult.hasError); } this.success = true; } } }