From 23f13fa0c3e3213af6e25b2d9b07bc97f1a609ec Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 11 Oct 2021 20:07:37 +0200 Subject: [PATCH] App Schema Auto Fix Feature --- Platform/Client/httpInterface.js | 177 +++++++++++++++--- Platform/UI/Utilities.js | 46 +++-- .../UI/Spaces/Docs-Space/CommandInterface.js | 14 ++ .../Schemas/App-Schema/lan-network.json | 3 +- .../UI/Function-Libraries/ActionSwitch.js | 2 +- .../Function-Libraries/UiObjectsFromNodes.js | 11 +- 6 files changed, 212 insertions(+), 41 deletions(-) diff --git a/Platform/Client/httpInterface.js b/Platform/Client/httpInterface.js index dd933ba67..f424472ee 100644 --- a/Platform/Client/httpInterface.js +++ b/Platform/Client/httpInterface.js @@ -899,20 +899,20 @@ exports.newHttpInterface = function newHttpInterface() { } } - async function runNodeSetup () { - console.log( "Running Node setup to adjust for new Branch" ) + async function runNodeSetup() { + console.log("Running Node setup to adjust for new Branch") const process = require("process"); const { execSync } = require("child_process"); let dir = process.cwd() let command = "node setup"; - let stdout = execSync( command, - { - cwd: dir - }).toString(); - + let stdout = execSync(command, + { + cwd: dir + }).toString(); + console.log("Node Setup has completed with the following result:", stdout) - + } } catch (err) { @@ -990,6 +990,138 @@ exports.newHttpInterface = function newHttpInterface() { } break } + + case 'FixAppSchema': { + /* + We will use this process when we have moved APP SCHEMA files from one project to another, and we need to fixt the + actions where this node was referenced, so that it points to the new project where the node has moved to. + */ + let customResponse = { + result: global.CUSTOM_OK_RESPONSE.result, + message: '' + } + SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(customResponse), httpResponse) + + console.log('Fixing App Schemas...') + + let projects = SA.projects.foundations.utilities.filesAndDirectories.getDirectories(global.env.PATH_TO_PROJECTS) + let PROJECTS_MAP = new Map() + let directoryCount = 0 + let allAppSchemas = [] + let allAppSchemasFilePaths = [] + + for (let i = 0; i < projects.length; i++) { + let project = projects[i] + + + + const fs = SA.nodeModules.fs + let folder = 'App-Schema' + let filePath = global.env.PATH_TO_PROJECTS + '/' + project + '/Schemas/' + SA.projects.foundations.utilities.filesAndDirectories.getAllFilesInDirectoryAndSubdirectories(filePath + folder, onFilesReady) + + function onFilesReady(files) { + try { + let SCHEMA_MAP = new Map() + + console.log('files.length... ' + files.length) + + for (let k = 0; k < files.length; k++) { + let name = files[k] + let nameSplitted = name.split(folder) + let fileName = nameSplitted[1] + for (let i = 0; i < 10; i++) { + fileName = fileName.replace('\\', '/') + } + let fileToRead = filePath + folder + fileName + + console.log('Reading file... ' + fileToRead) + + let fileContent = fs.readFileSync(fileToRead) + let schemaDocument + try { + schemaDocument = JSON.parse(fileContent) + SCHEMA_MAP.set(schemaDocument.type, schemaDocument) + allAppSchemas.push(schemaDocument) + allAppSchemasFilePaths.push(fileToRead) + } catch (err) { + console.log('[ERROR] sendSchema -> Error Parsing JSON File: ' + fileToRead + ' .Error = ' + err.stack) + return + } + } + PROJECTS_MAP.set(project, SCHEMA_MAP) + directoryCount++ + + console.log('directoryCount = ' + directoryCount, 'projects.length = ' + projects.length) + //console.log(Array.from(PROJECTS_MAP.get(project).keys())) + if (directoryCount === projects.length) { + fixSchemas() + } + } catch (err) { + console.log(err.stack) + } + } + } + + function fixSchemas() { + try { + console.log('fixSchemas...' + allAppSchemas.length) + let projects = SA.projects.foundations.utilities.filesAndDirectories.getDirectories(global.env.PATH_TO_PROJECTS) + const fs = SA.nodeModules.fs + let needFixing = 0 + for (let i = 0; i < allAppSchemas.length; i++) { + let schemaDocument = allAppSchemas[i] + let wasUpdated = false + for (let j = 0; j < schemaDocument.menuItems.length; j++) { + let menuItem = schemaDocument.menuItems[j] + if (menuItem.relatedUiObject !== undefined && menuItem.relatedUiObjectProject === undefined) { + needFixing++ + + let hits = 0 + let foundProject + let multiProject = '' + for (let k = 0; k < projects.length; k++) { + let project = projects[k] + + let testDocument = PROJECTS_MAP.get(project).get(menuItem.relatedUiObject) + if (testDocument !== undefined) { + hits++ + foundProject = project + multiProject = multiProject + ' -> ' + project + } + } + + if (hits === 0) { + console.log('Problem With No Solution #' + needFixing, ' Type: ' + schemaDocument.type, ' Action: ' + menuItem.action, ' Related UI Object: ' + menuItem.relatedUiObject) + console.log('This Node Type was NOT FOUND at any project. ' + menuItem.relatedUiObject) + continue + } + if (hits === 1) { + console.log('Problem With One Solution #' + needFixing, ' Type: ' + schemaDocument.type, ' Action: ' + menuItem.action, ' Related UI Object: ' + menuItem.relatedUiObject, ' Found Project:' + foundProject) + + menuItem.relatedUiObjectProject = foundProject + wasUpdated = true + continue + } + console.log('Problem With MULTIPLE Solutions #' + needFixing, ' Type: ' + schemaDocument.type, ' Action: ' + menuItem.action, ' Related UI Object: ' + menuItem.relatedUiObject, ' Found at these Projects:' + multiProject) + } + } + + if (wasUpdated === true) { + let fileContent = JSON.stringify(schemaDocument, undefined, 4) + let filePath = allAppSchemasFilePaths[i] + console.log('Saving File at ' + filePath) + console.log(fileContent) + fs.readFileSync(filePath, fileContent) + } + } + } catch (err) { + console.log(err.stack) + } + } + + break + } } function respondWithDocsObject(docs, error) { @@ -1406,13 +1538,13 @@ exports.newHttpInterface = function newHttpInterface() { try { let project = unescape(requestPath[2]) let folder = unescape(requestPath[3]) - + let response = await SA.projects.communityPlugins.utilities.plugins.getPluginFileNames( project, folder - ).catch(err => { + ).catch(err => { console.log('[ERROR] httpInterface -> PluginFileNames -> err.stack = ' + err.stack) - }) + }) SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(response), httpResponse) @@ -1445,25 +1577,24 @@ exports.newHttpInterface = function newHttpInterface() { if (fileName === 'Superalgos-CL.json') { fileName = 'Superalgos-PL.json' } - + await SA.projects.communityPlugins.utilities.plugins.getPluginFileContent( project, folder, fileName ) - .then( response => - { + .then(response => { SA.projects.foundations.utilities.httpResponses.respondWithContent(response, httpResponse) } - ) - .catch(err => { - let error = { - result: 'Fail Because', - message: err - } - SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(error), httpResponse) - return - }) + ) + .catch(err => { + let error = { + result: 'Fail Because', + message: err + } + SA.projects.foundations.utilities.httpResponses.respondWithContent(JSON.stringify(error), httpResponse) + return + }) } catch (err) { console.log('[ERROR] httpInterface -> LoadPlugin -> Method call produced an error.') diff --git a/Platform/UI/Utilities.js b/Platform/UI/Utilities.js index 21959ad07..b8be0cd45 100644 --- a/Platform/UI/Utilities.js +++ b/Platform/UI/Utilities.js @@ -1,4 +1,4 @@ -function getSchemaDocument (node, project) { +function getSchemaDocument(node, project) { if (node === null || node === undefined) { console.log('[ERROR] Can not get the schema document of a null or undefined node ') return @@ -7,13 +7,35 @@ function getSchemaDocument (node, project) { let APP_SCHEMA = SCHEMAS_BY_PROJECT.get(project) if (APP_SCHEMA !== undefined) { - return SCHEMAS_BY_PROJECT.get(project).map.appSchema.get(node.type) + + let schemaDocument = SCHEMAS_BY_PROJECT.get(project).map.appSchema.get(node.type) + /* + As of today, we are still splitting the codebase into multiple projects and there are Menu Actions which do not + have a well defined relatedUIObjectProject. There are a few techniques in place to try to guess it, but + even those can fail. For that reason, we will temporally see if we can find the schema document at any of the + available projects. + + TODO: We need to explictly declare the project of a relatedUIObject in order to avoid potential name collision + between nodes belonging to more than one project. + */ + + if (schemaDocument === undefined) { + for (let j = 0; j < PROJECTS_SCHEMA.length; j++) { + let project = PROJECTS_SCHEMA[j].name + schemaDocument = SCHEMAS_BY_PROJECT.get(project).map.appSchema.get(node.type) + if (schemaDocument !== undefined) { + break + } + } + } + + return schemaDocument } else { console.log('[ERROR] Could not get APP Schema for project ' + node.project) } } -function dynamicDecimals (value, minDecimals) { +function dynamicDecimals(value, minDecimals) { if (minDecimals === undefined) { minDecimals = 0 } @@ -26,7 +48,7 @@ function dynamicDecimals (value, minDecimals) { return returnValue } -function convertTimeFrameToName (pTimeFrame) { +function convertTimeFrameToName(pTimeFrame) { for (let i = 0; i < dailyTimeFramesArray.length; i++) { let period = dailyTimeFramesArray[i] if (period[0] === pTimeFrame) { @@ -42,7 +64,7 @@ function convertTimeFrameToName (pTimeFrame) { } } -function nextPorwerOf10 (number) { +function nextPorwerOf10(number) { for (let i = -10; i <= 10; i++) { if (number < Math.pow(10, i)) { return Math.pow(10, i) @@ -50,18 +72,18 @@ function nextPorwerOf10 (number) { } } -function pad (str, max) { +function pad(str, max) { str = str.toString() return str.length < max ? pad('0' + str, max) : str } -function moveToUserPosition (container, currentDate, currentRate, coordinateSystem, ignoreX, ignoreY, mousePosition, fitFunction) { +function moveToUserPosition(container, currentDate, currentRate, coordinateSystem, ignoreX, ignoreY, mousePosition, fitFunction) { let targetPoint = { x: currentDate.valueOf(), y: currentRate } - /* Put this point in the coordinate system of the UI.projects.foundations.spaces.chartingSpace.viewport */ + /* Put this point in the coordinate system of the UI.projects.foundations.spaces.chartingSpace.viewport */ targetPoint = coordinateSystem.transformThisPoint(targetPoint) targetPoint = UI.projects.foundations.utilities.coordinateTransformations.transformThisPoint(targetPoint, container) @@ -81,7 +103,7 @@ function moveToUserPosition (container, currentDate, currentRate, coordinateSyst container.displace(displaceVector) } -function removeTime (datetime) { +function removeTime(datetime) { if (datetime === undefined) { return } let dateOnly = new Date(Math.trunc(datetime.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) @@ -89,14 +111,14 @@ function removeTime (datetime) { return dateOnly } -function newUniqueId () { +function newUniqueId() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8) return v.toString(16) }) } -function splitTextIntoPhrases (text, wordsPerLine) { +function splitTextIntoPhrases(text, wordsPerLine) { try { if (text.split === undefined) { return [] @@ -128,7 +150,7 @@ function splitTextIntoPhrases (text, wordsPerLine) { } } -function getTextWidth (text) { +function getTextWidth(text) { var metrics = browserCanvasContext.measureText(text) return metrics.width } diff --git a/Projects/Education/UI/Spaces/Docs-Space/CommandInterface.js b/Projects/Education/UI/Spaces/Docs-Space/CommandInterface.js index 2568451b8..37b0a6dd4 100644 --- a/Projects/Education/UI/Spaces/Docs-Space/CommandInterface.js +++ b/Projects/Education/UI/Spaces/Docs-Space/CommandInterface.js @@ -27,6 +27,7 @@ function newFoundationsDocsCommmandInterface() { function detectAppCommands() { if (checkContributeCommand() === undefined) { return true } if (checkUpdateCommand() === undefined) { return true } + if (fixAppSchema() === undefined) { return true } function checkContributeCommand() { if (UI.projects.education.spaces.docsSpace.commandInterface.command.toLowerCase() === 'app.help app.contribute') { @@ -130,6 +131,19 @@ function newFoundationsDocsCommmandInterface() { } } } + + function fixAppSchema() { + + if (UI.projects.education.spaces.docsSpace.commandInterface.command.indexOf('App.fix') !== 0 && UI.projects.education.spaces.docsSpace.commandInterface.command.indexOf('app.fix') !== 0) { return 'Not Update Commands' } + + httpRequest(undefined, 'App/FixAppSchema', onResponse) + + return + + function onResponse(err, data) { + + } + } } function detectDocsCommands() { diff --git a/Projects/Foundations/Schemas/App-Schema/lan-network.json b/Projects/Foundations/Schemas/App-Schema/lan-network.json index 50307a9fa..1b90ed03e 100644 --- a/Projects/Foundations/Schemas/App-Schema/lan-network.json +++ b/Projects/Foundations/Schemas/App-Schema/lan-network.json @@ -5,12 +5,13 @@ "action": "Add UI Object", "label": "Add LAN Network Node", "relatedUiObject": "LAN Network Node", + "relatedUiObjectProject": "Foundations", "actionFunction": "payload.executeAction", "actionProject": "Visual-Scripting" }, { "action": "Delete UI Object", -"actionProject": "Visual-Scripting", + "actionProject": "Visual-Scripting", "askConfirmation": true, "confirmationLabel": "Confirm to Delete", "label": "Delete", diff --git a/Projects/Visual-Scripting/UI/Function-Libraries/ActionSwitch.js b/Projects/Visual-Scripting/UI/Function-Libraries/ActionSwitch.js index 7bd36a2df..9f2ef055e 100644 --- a/Projects/Visual-Scripting/UI/Function-Libraries/ActionSwitch.js +++ b/Projects/Visual-Scripting/UI/Function-Libraries/ActionSwitch.js @@ -1,4 +1,4 @@ -function newFoundationsActionSwitch() { +function newVisualScriptingActionSwitch() { let thisObject = { executeAction: executeAction, diff --git a/Projects/Visual-Scripting/UI/Function-Libraries/UiObjectsFromNodes.js b/Projects/Visual-Scripting/UI/Function-Libraries/UiObjectsFromNodes.js index 7f432f813..4bdf462d2 100644 --- a/Projects/Visual-Scripting/UI/Function-Libraries/UiObjectsFromNodes.js +++ b/Projects/Visual-Scripting/UI/Function-Libraries/UiObjectsFromNodes.js @@ -186,7 +186,7 @@ function newVisualScritingFunctionLibraryUiObjectsFromNodes() { for (let i = 0; i < node.rootNodes.length; i++) { let rootNode = node.rootNodes[i] if (rootNode.id === receivedNode.id) { - console.log('[WARN] The node with name "' + rootNode.name + '" and type "' + rootNode.type + '" will be replaced by the node with name "' + receivedNode.name + '" and type "' + receivedNode.type +'" because they both have the same node.id') + console.log('[WARN] The node with name "' + rootNode.name + '" and type "' + rootNode.type + '" will be replaced by the node with name "' + receivedNode.name + '" and type "' + receivedNode.type + '" because they both have the same node.id') node.rootNodes.splice(i, 1) break } @@ -601,8 +601,11 @@ function newVisualScritingFunctionLibraryUiObjectsFromNodes() { let schemaDocument = getSchemaDocument(object, project) if (schemaDocument === undefined) { - console.log('Cannot addUIOBject of type "' + type + '" and project "' + project + '" because that type it is not defined at the APP_SCHEMA.') - return + schemaDocument = getSchemaDocument(object, 'Foundations') // TODO: This is provisional until all actions can have the relatedUiObject project explicitly set. + if (schemaDocument === undefined) { + console.log('Cannot addUIOBject of type "' + type + '" and project "' + project + '" because that type it is not defined at the APP_SCHEMA.') + return + } } if (schemaDocument.initialValues !== undefined) { @@ -767,7 +770,7 @@ function newVisualScritingFunctionLibraryUiObjectsFromNodes() { if (property.type === 'node') { if (property.name !== previousPropertyName) { if (node[property.name] === undefined) { - let currenAngleToParent = node.payload.floatingObject.angleToParent + let currenAngleToParent = node.payload.floatingObject.angleToParent node.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180 let uiObject = addUIObject(node, property.childType, rootNodes, property.project) node.payload.floatingObject.angleToParent = currenAngleToParent