FrenchYeti/dexcalibur

View on GitHub
src/Inspector.js

Summary

Maintainability
B
4 hrs
Test Coverage
const Logger = require("./Logger.js")();
const IFC = require("./InspectorFrontController.js");
const fs = require("fs");
const Path = require("path");
const InMemoryDb = require("../connectors/inmemory/InMemoryDb.js").InMemoryDb;
//const UT = require("./Utils.js");

const TASK_CODE = {
    NO_RESULT: 0,
    SUCCESS: 1,
    DATA_UPDATE: 2
};

const TYPE = {
    BOOT: 'BOOT',
    POST_APP_SCAN: 'POST_APP_SCAN',
    POST_PLATFORM_SCAN: 'POST_PLATFORM_SCAN',
    POST_DEV_SCAN: 'POST_DEV_SCAN',
    ON_DEMAND: 'ON_DEMAND',
};

class StaticTask{

    constructor(config){
        this.task = null;
        this.condition = null;
        this.onDataUpdate = null;
        this.onNoResult = null;
        this.onSuccess = null;

        for(let i in config) this[i] = config[i];
    }

    setCondition(fn){
        this.task = fn;
    } 

    setTask(fn){
        this.condition = fn;
    }

    onSuccess(fn){
        this.onSuccess = fn;
    }

    onNoResult(fn){
        this.onNoResult = fn;
    }

    onDataUpdate(fn){
        this.onDataUpdate = fn;
    }

    exec(ctx, event){
        if(this.condition != null && this.condition(ctx))
            this.task(ctx, event);
        else
            this.task(ctx, event);
    }
}



class Inspector{
    
    constructor(config){
        this.id = null;
        this.name = null;
        this.description = null;

        this.context = null;
        this.hookSet = null;
        this.staticTasks = null;
        this.running = false;
        this.listener = {};
        this.events = [];
        this.gui_available = false;
        this.frontController = null;
        this.preRegisteredTags = [];
        this.db = null;

        /**
         * @type {String}
         * @field
         */
        this.color = null;

        this.installed = false;
        this.step = TYPE.BOOT;

        for(let i in config){
            this[i] = config[i];
            if(i==="hookSet"){
                this.id = config[i].id;
                this.name = config[i].name;
                this.description = config[i].description;
            }
        }

        return this;
    }


    isInstalled(){
        return this.installed;
    }

    useGUI(){
        this.gui_available = true;
    }

    useMemoryDB( pConnector = null){
        this.db = new InMemoryDb(pConnector);

        return this.db;
    }

    getDB(){
        return this.db;
    }


    /**
     * To forward an HTTP GET request from the web server handler to the inspector front controller if available
     * Internal use
     */
    performGet(req,res){
        if(this.frontController.hasHandler(IFC.HANDLER.GET)){
            return this.frontController.performGet(req,res);
        }else{
            return { error: true, msg:"Unavailable GET handler for this inspector" };
        }
    }


    /**
     * To forward an HTTP POST request from the web server handler to the inspector front controller if available
     * Internal use
     */
    performPost(req,res){
        if(this.frontController.hasHandler(IFC.HANDLER.POST)){
            return this.frontController.performPost(req,res);
        }else{
            return { error: true, msg:"Unavailable POST handler for this inspector" };
        }
    }

    /**
     * To invoke the StaticTask instances associated to the given event. 
     */
    broadcastEvent(event){
        let event_type = event.type;

        //Logger.info( event_type, this.listener[event_type]);
        //console.log(this.listener);
        if(this.listener[event_type] != null){
            for(let i=0; i<this.listener[event_type].length; i++){
                // TODO : async / co
                // console.log(this.listener[event_type][i]);
                this.listener[event_type][i].exec(this.context, event, this);
            }
        }
        return true;
    }

    /**
     * To declare new event handler. If the param `task`is  a function, a new StaticTask is create implicitly.
     */
    on(event_type, task){
        if(this.listener[event_type] == null)
            this.listener[event_type] = [];
        
        if(task instanceof StaticTask)
            this.listener[event_type].push(task);
        else
            this.listener[event_type].push(new StaticTask(task));
            
        return this;
    }


    registerTagCategory(name,tags){
        this.preRegisteredTags.push({ name:name, tags:tags });
    }


    /**
     * @method
     */
    injectContext(ctx){
        this.context = ctx;

        if(this.hookSet != null){
            this.hookSet.injectContext(ctx);
            Logger.info("[Inspector::injectContext][HookSet] "+this.id+" registered !");
        }else{
            Logger.info("[Inspector::injectContext] "+this.id+" has not hook set.");
        }

        // register front controller
        let path = Path.join(__dirname,"..","inspectors",this.id,"service","main.js");
        if(fs.existsSync(path)){
            this.frontController = require(path).injectContext(ctx);
            Logger.info("[Inspector::injectContext][FrontController] "+this.id+" registered !");
        }
        /*
        else if(Path.basename(ctx.config.getDexcaliburPath()) !== "src"){
            path = Path.join(ctx.config.getDexcaliburPath(),"inspectors",this.id,"service","main.js");
            if(fs.existsSync(path)){
                this.frontController = require(path).injectContext(ctx);
                Logger.info("[Inspector::injectContext][FrontController] "+this.id+" registered !");
            }
        }*/


        // declare TagCategory
        let anal = ctx.getAnalyzer();
        for(let i=0; i<this.preRegisteredTags.length; i++){
            anal.addTagCategory(this.preRegisteredTags[i].name, this.preRegisteredTags[i].tags)
        }

        /*if(this.db instanceof InMemoryDb){
            //this.db.setContext(this.context);
        }*/

        return this;
    }

    setStartStep(pStep){
        this.step = pStep;
    }

    getStartStep(){
        return this.step;
    }

    setHookSet(hs){
        this.hookSet = hs;
        this.hookSet.color = this.color;

        if(this.id==null) this.id = hs.id;
        if(this.name==null) this.name = hs.name;
        if(this.description==null) this.description = hs.description;
    }

    getHookSet(){
        return this.hookSet;
    }

    getStaticTasks(){
        return this.staticTasks;
    }

    getStaticTask(name){
        return this.staticTasks[name];
    }

    getID(){
        return this.id;
    }

    deploy(){
        this.running = true;
        this.hookSet.deploy();
    };

    // Inspector life-cycle
    /*
    turnOn(){
        this.running = true;
        if(this.hookSet!=null)
            this.hookSet.enable();
    }

    turnOff(){
        return this.staticTasks[name];
        if(this.hookSet!=null)
            this.hookSet.disable();
    }*/

    /**
     * emit a new event on the main event bus
     */
    emits(name,event){
        if(this.events.indexOf(name)===-1)
            this.events.push(name);

        this.context.bus.send({ 
            type: name, 
            data: event 
        });
    }

    /**
     * 
     * @param {*} pStep
     * @method 
     */
    isStartAt(pStep){
        return (this.step === pStep)
    }

    /**
     * 
     * @param {*} callback 
     * @method
     */
    restore(callback=null){
        let self = this;
        let savePath = Path.join(this.context.workspace.getSaveDir(), this.id+".json");
        fs.exists(savePath, function(exist){
            if(!exist){
                return ;
            }
            
            fs.readFile(savePath, 'ascii', function(err, data){
                let o = JSON.parse(data);
                self.db.unserialize(o);
                if(typeof callback === 'function')
                    callback(self);
            })
        })
    }

    /**
     * @method
     */
    save(){
        if(!this.db instanceof InMemoryDb) return null;

        let self = this;
        let savePath = Path.join(this.context.workspace.getSaveDir(), this.id+".json");
        fs.exists(savePath, function(exist){
            if(exist){
                fs.unlinkSync(savePath);
            }
            
            fs.open(savePath, 'w+', function(err, fd){
                if(err){
                    console.log("Save file cannot be created");
                    return;
                }

                let data = self.db.serialize();
                fs.write(fd, JSON.stringify(data), function(err){
                    if(err){
                        console.log("Save file cannot be created");
                        return;
                    }
                    console.log("Inspector "+self.id+" backed up");
                    fs.close(fd,function(err){});
                });
            })
        })
    }

    /**
     * To cast the current object to an object ready to be serialize (it avoids cyclic reference)
     * @returns {Object} 
     * @method
     */
    toJsonObject(){
        let o = {};
        o.id = this.id;
        o.description = this.description;
        o.name = this.name;
        o.running = this.running;
        o.events = this.events;
        o.hooks = this.hookSet.toJsonObject();
        o.listener = [];
        o.gui_available = this.gui_available;
        for(let i in this.listener)
            o.listener.push({ n:i });
        
        return o;
    }
}


module.exports = {
 //   InspectorController: InspectorController,
    Inspector: Inspector,
    StaticTask: StaticTask,
    RET: TASK_CODE,
    STEP: TYPE
};