'use strict'; const fs = require('fs'); function Parser(options) { var self = this; options = options || {}; function debug(message) { if (self.params.debug_mode) { console.log(`[DEBUG] ${message}`); } } this.defaults = { path : './data.json', query_delim : '/', debug_mode : false } this.params = Object.assign({}, self.defaults, options); this.data = null; this.test = { 'valid_deal' : function(query) { return self.get.deals(query) .then(function(data) { if (query == "") return false; if (data.length > 0) { return true; } return false; }) }, 'valid_cluster' : function(query) { return self.test.has_hosts(query) }, 'valid_host' : function(query) { return self.test.has_hosts(query) }, 'has_hosts' : function(query) { return self.get.hosts(query) .then(host_list => { if (query == "") return false; debug("Host list: "+host_list); if (host_list.length > 0) { return true; } return false; }) }, 'is_object' : function(obj) { return (typeof obj === 'object' && obj !== null); } } this.xform = { 'query_str_to_arr' : function(str = "") { if (!str || str == "" || str == '') { debug("Transforming empty search string."); return []; } var arr = str.split(self.params.query_delim); var split_str="" arr.forEach(term => { split_str = split_str + " " + term; }) arr = split_str.split(" "); arr = arr.filter(term => term != '.'); arr = arr.filter(term => term != ''); debug("Query arr: "+arr); return arr; } } this.reduce = { 'once' : function(data = {},name = "") { const addressed = self.address(data,[...name]); return addressed.data; }, 'fuzzy' : function(data = {},names = []) { if (!Array.isArray(names) || names.length < 1) { return data; } var name = names.shift(); var result = self.reduce.once(data,name); names.forEach(name => { result = [...result.map(tree => { const matches = self.reduce.once(tree,name) debug(matches); return matches; })]}) result.filter(obj => obj); return result; }, 'exact' : function(data = {},names = []) { if (!Array.isArray(names) || names.length < 1) { return data; } const addressed = self.address(data,names); debug("Reduced exact data: "+addressed); return addressed.data; } } this.address = function(data = {},names = []) { if (!names || names.length < 1 || !data || data == {}) { debug("nope"); return { paths: [['.']], data: [data] }; } var match_paths = new Array(); var match_data = new Array(); var branches = new Array({ root: ".", data: data }); for(var i=0; i < branches.length; i++) { const data = branches[i].data; const root = branches[i].root; for (const [name,value] of Object.entries(data)) { if (self.test.is_object(value)) { branches.push({ root: [...root,name], data: value }); } if (name == names[0]) { var match = data; names.forEach(term => { debug("Searching for term: "+term); if (match && match[term]) { match = match[term]; } else { match = undefined } }); if (match) { const match_path=[...root] match_paths.push(match_path) match_data.push(match) debug("match found:" +match_path+"; "+match); } } } } return { paths: match_paths, data: match_data }; } this.get = { 'raw' : function(){ return new Promise(function(resolve, reject){ if(!self.data){ fs.readFile( self.params.path, function(err, data) { if(err) reject(err); try { JSON.parse(data); self.data = JSON.parse(data) resolve(self.data) } catch(err){ reject(err) } } ) } else { resolve(self.data); } }) }, 'reduced' : function(names = [],ascent = [],descent = []) { return self.get.raw().then(entire_data => { debug(`ascent: ${ascent}`); debug(`names: ${names}`); debug(`descent: ${descent}`); if (names.length+ascent.length+descent.length<1) { return entire_data; } const domain = self.reduce.fuzzy(entire_data,ascent); const fence = self.reduce.exact(domain,names); const product = self.reduce.fuzzy(fence,descent); return product; //return self.reduce.exact(entire_data,names); }) }, 'addressed' : function(names) { return self.get.raw().then(entire_data => { debug(`Addressing raw: ${JSON.stringify(names)}`); return self.address(entire_data,names); }) }, 'deals' : function(query){ return self.get.addressed( [...(self.xform.query_str_to_arr(query)),"repo"] ).then(addressed => { return addressed.paths; }) }, 'hosts' : function(query){ return self.get.addressed( [...(self.xform.query_str_to_arr(query))] ).then(cluster_data => { debug("Cluster search data: " +JSON.stringify(cluster_data)) const host_matches = cluster_data.data.map(data =>{ debug("Data in cluster: "+JSON.stringify(data)) const hosts = self.address( data, ["remote","host"] ); return hosts.paths; }) return host_matches; }) }, 'host_list': function(query) { return self.get.hosts(query).then(paths => { const host_list = paths.map(path_list => { const return_queries = path_list.map(path_arr => { var query_str = ""; path_arr = path_arr.filter(e => e != '.'); path_arr.forEach(e => { query_str += e+" "; }) return query_str.trim(); }) return return_queries; }) return host_list; }) }, 'root' : function(query){ return self.get.addressed( [...(self.xform.query_str_to_arr(query))] ).then(addressed => { return addressed.paths; }) }, 'path' : function(query) { return self.get.root(query).then(roots => { var paths = new Array(); debug(`Roots found: ${roots}`); roots.forEach ( function(root, i, array) { var str = ""; debug(`Formatting ${root}`); root.forEach( function(key,j,arr) { debug(`${j}: ${key}`); str = str + "/" + key; } ) debug(str); var query_arr = query.split(" "); query_arr.forEach( function(term,j,arr) { str = str + "/" + term; }) str = str.slice(3) paths.push(str); } ); return paths; }) }, 'cluster_data' : function(query) { return self.get.reduced( self.xform.query_str_to_arr(query), ["clusters"], ["ports","ssh"] ).then(data => { return data; }) }, 'data' : function(query) { return self.get.reduced( self.xform.query_str_to_arr(query) ).then(data => { return data; }) } } }; module.exports = Parser;