diff --git a/.vs/AACloudPlatform/v15/.suo b/.vs/AACloudPlatform/v15/.suo index 65e2cac23..8080165c5 100644 Binary files a/.vs/AACloudPlatform/v15/.suo and b/.vs/AACloudPlatform/v15/.suo differ diff --git a/Project.njsproj b/Project.njsproj index f88c2ef97..45489f9dc 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -38,6 +38,9 @@ + + Code + Code diff --git a/Status Report.js b/Status Report.js new file mode 100644 index 000000000..962032f57 --- /dev/null +++ b/Status Report.js @@ -0,0 +1,247 @@ +exports.newStatusReport = function newStatusReport(BOT, DEBUG_MODULE, FILE_STORAGE, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + /* + + A Status Report is a file that every bot reads at the begining of its execution and saves after it finishes its job. + The purpose of the file is to record checkpoint information of what was the last thing done by the bot and helpfull enough to start the next execution. + It usually does not include business related context data. + + */ + + const MODULE_NAME = "Status Report"; + + let thisObject = { + file: undefined, // Here we have the JSON object representing the file content. + initialize: initialize, + save: save + }; + + /* + + During the process we will create a new History Record. This will go to the Context History file which essentially mantains an + index of all the bots executions. This file will later be plotted by the bot s plotter on the timeline, allowing end users to + know where there is information related to the actions taken by the bot. + + */ + + let bot = BOT; + let ownerBot; // This is the bot owner of the Status Report. Only owners can save the report and override the existing content. + + const logger = DEBUG_MODULE.newDebugLog(); + logger.fileName = MODULE_NAME; + logger.bot = bot; + + /* Utilities needed. */ + + let utilities = UTILITIES.newUtilities(bot); + + /* Storage account to be used here. */ + + let cloudStorage = FILE_STORAGE.newAzureFileStorage(bot); + + return thisObject; + + function initialize(pOwnerBot, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } + + /* + + Here we get the positions the bot did and that are recorded at the bot storage account. We will use them through out the rest + of the process. + + */ + + ownerBot = pOwnerBot; + + initializeStorage(); + + function initializeStorage() { + + cloudStorage.initialize(bot.codeName, onInizialized); + + function onInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + getFile(onDone); + + } else { + logger.write("[ERROR] initialize -> initializeStorage -> onInizialized -> err = " + err.message); + callBackFunction(err); + } + } + } + + function onDone(err) { + try { + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write("[INFO] initialize -> onDone -> Execution finished well. :-)"); + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + break; + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write("[ERROR] initialize -> onDone -> Retry Later. Requesting Execution Retry."); + callBackFunction(err); + return; + } + break; + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write("[ERROR] initialize -> onDone -> Operation Failed. Aborting the process."); + callBackFunction(err); + return; + } + break; + } + + } catch (err) { + logger.write("[ERROR] initialize -> onDone -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getFile(callBack) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> Entering function."); } + + let fileName = "Status.Report.json" + let filePath = ownerBot.devTeam + "/" + ownerBot.codeName + "." + ownerBot.version.major + "." + ownerBot.version.minor + "/" + ownerBot.dataSetVersion + "/Processes/" + ownerBot.process + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor; + + if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> fileName = " + fileName); } + if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> filePath = " + filePath); } + + cloudStorage.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + if (err.result === global.DEFAULT_FAIL_RESPONSE.result && err.message === 'Folder does not exist.') { + logger.write("[INFO] initialize -> getFile -> onFileReceived -> err = " + err.message); + + /* In this case we can assume that this is the first execution ever of this bot.*/ + + createConext(callBack); + return; + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write("[ERROR] initialize -> getFile -> onFileReceived -> err = " + err.message); + callBack(err); + return; + } + + if (FULL_LOG === true) { + logger.write("[INFO] initialize -> getFile -> onFileReceived -> Content received = " + text); + } + + try { + + thisObject.statusReport = JSON.parse(text); + getExecutionHistory(callBack); + + } catch (err) { + + /* + It might happen that the file content is corrupt. We will consider this as a temporary situation, since sometimes the file + is being updated at the moment of the read. The bot can not run without a valid Status Report but we can request the platform to retry later. + */ + + logger.write("[ERROR] initialize -> getFile -> onFileReceived -> Bot cannot execute without a valid Status report. -> Err = " + err.message); + callBack(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write("[ERROR] initialize -> getFile -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write("[ERROR] initialize -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function save(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] save -> Entering function."); } + + let ownerId = ownerBot.devTeam + "-" + ownerBot.codeName; + let botId = bot.devTeam + "-" + bot.codeName; + + if (ownerId !== botId) { + + let err = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: "Only bots owners of a Status Report can save them." + } + logger.write("[ERROR] save -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + let fileName = "Status.Report.json" + let filePath = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + bot.dataSetVersion + "/Processes/" + bot.process + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor; + + if (FULL_LOG === true) { logger.write("[INFO] save -> fileName = " + fileName); } + if (FULL_LOG === true) { logger.write("[INFO] save -> filePath = " + filePath); } + + utilities.createFolderIfNeeded(filePath, cloudStorage, onFolderCreated); + + function onFolderCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] save -> onFolderCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write("[ERROR] save -> onFolderCreated -> err = " + err.message); + callBackFunction(err); + return; + } + + let fileContent = JSON.stringify(thisObject.statusReport); + + cloudStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write("[INFO] save -> onFolderCreated -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write("[ERROR] save -> onFolderCreated -> onFileCreated -> err = " + err.message); + callBackFunction(err); + return; + } + + if (FULL_LOG === true) { + logger.write("[INFO] save -> onFolderCreated -> onFileCreated -> Content written = " + fileContent); + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + } + catch (err) { + logger.write("[ERROR] save -> onFolderCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write("[ERROR] save -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; \ No newline at end of file diff --git a/platform.config.json b/platform.config.json index 95ce357d7..9b6050224 100644 --- a/platform.config.json +++ b/platform.config.json @@ -1,12 +1,12 @@ { - "codeName": "AACloudPlatform", + "codeName": "AACloud", "version": { "major": 1, "minor": 0, - "patch": 0 + "patch": 1 }, "bot": { - "path": "../Bots/AAMasters/AAMariam-Trading-Bot" + "path": "../Bots/AAMasters/AAOlivia-Indicator-Bot" }, "stopGracefully": "false" } \ No newline at end of file