UNPKG

@itentialopensource/adapter-apache_airflow

Version:

This adapter integrates with system described as: Apache Airflow

274 lines (249 loc) 9.46 kB
/* eslint global-require:warn */ /* eslint import/no-dynamic-require:warn */ /* eslint no-param-reassign:warn */ const fs = require('fs-extra'); const acorn = require('acorn'); // Getting the base directory: let adaptdir = __dirname; if (adaptdir.endsWith('/utils')) { adaptdir = adaptdir.substring(0, adaptdir.length - 6); } function createObjectForFunction( funcName, funcArgs, entityPath, description, workflow ) { const funcObject = {}; // if the entity path is not set, then the object is not created. if (entityPath !== undefined) { funcObject.method_signature = `${funcName}(${funcArgs.join(', ')})`; funcObject.path = entityPath; if (description === undefined) { funcObject.description = ''; funcObject.workflow = 'No'; } else { funcObject.description = description; funcObject.workflow = workflow; } } return funcObject; } function getPathFromEntity(entity, funcName) { let epath; if (entity === undefined || entity === '.generic') { epath = undefined; } else { // Access the action.js file for the certain entity to get the path const entityPath = `${adaptdir}/entities/${entity}/action.json`; const actionJSON = require(entityPath); actionJSON.actions.forEach((action) => { if (action.name === funcName) { if (typeof action.entitypath === 'object') { epath = ''; const keys = Object.keys(action.entitypath); for (let k = 0; k < keys.length; k += 1) { epath += `${keys[k]}:${action.entitypath[keys[k]]} <br /> `; } epath = epath.substring(0, epath.length - 8); } else { epath = action.entitypath; } } }); } return epath; } function recurseCallExpressions(statement, callList) { // Recursively finds all CallExpressions in the syntax tree if (statement.type === 'CallExpression') callList.push(statement); const keys = Object.keys(statement); for (let k = 0; k < keys.length; k += 1) { if (typeof statement[keys[k]] === 'object' && statement[keys[k]] !== null) { recurseCallExpressions(statement[keys[k]], callList); } } } function readFileUsingLib(filename, descriptionObj, workflowObj, functionList) { // read the file const aFile = fs.readFileSync(filename, 'utf8'); // parsing the file to get the function and class declarations. const aFileFuncArgs = acorn.parse(aFile, { ecmaVersion: 2020 }); let callName = 'identifyRequest'; // Looping through all the declarations parsed: aFileFuncArgs.body.forEach((e) => { // Getting only the class declaration as it has our required functions. if (e.type === 'ClassDeclaration') { const methodDefinition = e.body; methodDefinition.body.forEach((method) => { // Getting method name and its params in the class. const funcName = method.key.name; const funcArgs = []; method.value.params.forEach((param) => { if (param.type === 'Identifier') { funcArgs.push(param.name); } else if (param.type === 'RestElement') { funcArgs.push(`...${param.argument.name}`); } else { const args = `${param.left.name} = ${param.right.raw}`; funcArgs.push(args); } }); // Getting the entity for the method: const callList = []; method.value.body.body.forEach((statement) => { recurseCallExpressions(statement, callList); }); const requests = []; for (let i = 0; i < callList.length; i += 1) { if (callList[i].callee.property && callList[i].callee.property.name === callName) { requests.push(callList[i]); } } if (requests.length > 0) { const expr = requests[0]; if (expr.arguments.length < 2) { throw new Error(`Bad inputs in method ${funcName}`); } const entity = expr.arguments[0].value; const actionName = expr.arguments[1].value; if (expr !== undefined && (expr.arguments[0].type !== 'Literal' || expr.arguments[1].type !== 'Literal')) { const param1 = method.value.params[0]; const param2 = method.value.params[1]; if (param1.type !== 'Identifier' || param2.type !== 'Identifier' || expr.arguments[0].type !== 'Identifier' || expr.arguments[1].type !== 'Identifier' || param1.name !== expr.arguments[0].name || param2.name !== expr.arguments[1].name) { throw new Error(`identifyRequest proxy method ${funcName} unknown format`); } else if (callName !== 'identifyRequest') { throw new Error(`MethodDocumentor not yet programmed to handle multiple helper methods: 1) ${callName}, 2) ${funcName}`); } callName = funcName; } const entityPath = getPathFromEntity(entity, actionName); // Creating and storing the object for the method. if (entityPath !== undefined) { functionList.push( createObjectForFunction( funcName, funcArgs, entityPath, descriptionObj[funcName], workflowObj[funcName] ) ); } } }); } }); } function readJSONFile(filename, descriptionObj, workflowObj) { // Accessing the JSON file. const phJSON = require(filename); // Getting the methods array. const methodArray = phJSON.methods; methodArray.forEach((methodName) => { // Getting the method description and workflow: const funcName = methodName.name; descriptionObj[funcName] = methodName.summary ? methodName.summary : methodName.description; workflowObj[funcName] = methodName.task ? 'Yes' : 'No'; }); } function readMDFile(filename, functionList) { // Reading in the .md file and creating an array with each line as an element. const mdFile = fs.readFileSync(filename, 'utf-8'); const fileSplit = mdFile.split('\n'); // Storing the data that should added later to the updated data. const linesToAddLater = []; let index = fileSplit.length - 1; // Removing all the blank lines at the end of the file. if (fileSplit[index] === '') { while (fileSplit[index] === '') { linesToAddLater.push(fileSplit.pop()); index -= 1; } } // Checking if the last 2 lines are <br> and </table>. If not, the file is corrupted and the // data at the end of the file should be fixed. if (fileSplit[index] === '<br>' || fileSplit[index - 1] === '</table>') { // Storing <br> and </table> to add later. linesToAddLater.push(fileSplit.pop()); linesToAddLater.push(fileSplit.pop()); index -= 2; } else { console.log('The file has bad content at the end.'); return; } // if (fileSplit[index] !== '<br>' && fileSplit[index - 1] !== '</table>') { // console.log('The file has bad content at the end.'); // return; // } else { // // Storing <br> and </table> to add later. // linesToAddLater.push(fileSplit.pop()); // linesToAddLater.push(fileSplit.pop()); // index -= 2; // } // Removing all the lines until the header tags are reached. while (!fileSplit[index].includes('<th')) { fileSplit.pop(); index -= 1; } // Adding </tr> for the header row, because it got removed in the above loop. fileSplit.push(' </tr>'); // Creating the tags for each method to be appended to the file. const tdBeginTag = ' <td style="padding:15px">'; const tdEndTag = '</td>'; functionList.forEach((func) => { const signCommand = `${tdBeginTag}${func.method_signature}${tdEndTag}`; const descCommand = `${tdBeginTag}${func.description}${tdEndTag}`; const pathCommand = `${tdBeginTag}${func.path}${tdEndTag}`; const workflowCommand = `${tdBeginTag}${func.workflow}${tdEndTag}`; fileSplit.push(' <tr>'); fileSplit.push(signCommand); fileSplit.push(descCommand); fileSplit.push(pathCommand); fileSplit.push(workflowCommand); fileSplit.push(' </tr>'); }); // Adding </table> and <br> at the end of the file to complete the table and the file. while (linesToAddLater.length > 0) { fileSplit.push(linesToAddLater.pop()); } // Writing all the content back into the file. fs.writeFileSync(filename, fileSplit.join('\n'), { encoding: 'utf-8', flag: 'w' }); } function getFileInfo() { // If files don't exist: if (!fs.existsSync(`${adaptdir}/adapter.js`)) { console.log('Missing - utils/adapter.js'); return; } if (!fs.existsSync(`${adaptdir}/pronghorn.json`)) { console.log('Missing - pronghorn.json'); return; } if (!fs.existsSync(`${adaptdir}/CALLS.md`)) { console.log('Missing - CALLS.md'); return; } const descriptionObj = {}; const workflowObj = {}; // Get the method descriptions and the workflow values from pronghorn.json file. readJSONFile(`${adaptdir}/pronghorn.json`, descriptionObj, workflowObj); // Get the method signature, entity path and create an object that contains all the info regarding // the method and push it to the functionList array. const functionList = []; readFileUsingLib( `${adaptdir}/adapter.js`, descriptionObj, workflowObj, functionList ); // createMarkDown(functionList); readMDFile(`${adaptdir}/CALLS.md`, functionList); } getFileInfo();