toggle-corp/react-store

View on GitHub
utils/coordinate/Coordinator.js

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * @author frozenhelium
 * @co-author tnagorra <weathermist@gmail.com>
 */

export default class Coordinator {
    constructor(preSession = undefined, postSession = undefined, maxActiveActors = 3) {
        this.maxActiveActors = maxActiveActors;
        this.preSession = preSession;
        this.postSession = postSession;

        // stores all the actors to be started
        // once started, it is moved to activeActors
        this.queuedActors = [];
        // stores all the actors that has started
        // once completed, it is just removed
        this.activeActors = [];

        // NOTE:
        // If all the actors immediately fail then,
        // at the time when the notifyComplete is called
        // the activeActors and queuedActors are both empty which
        // results in multiple postSession calls.
        // To prevent this, memorize the session state
        this.inSession = false;
        this.totalErrors = 0;
    }

    // INTERNAL
    getActorById = id => [...this.queuedActors, ...this.activeActors].find(
        actor => actor.id === id,
    )

    // INTERNAL
    getQueuedActorIndexById = id => this.queuedActors.findIndex(
        actor => actor.id === id,
    )

    // INTERNAL
    getActiveActorIndexById = id => this.activeActors.findIndex(
        actor => actor.id === id,
    )

    add = (id, nativeActor) => {
        const oldActor = this.getActorById(id);
        if (oldActor !== undefined) {
            // console.warn(`Uploder with id '${id}' already registered.`);
            return false;
        }

        // create upload wrapper
        const actor = { id, nativeActor };

        // add upload wrapper to list
        this.queuedActors.push(actor);
        return true;
    }

    remove = (id) => {
        const indexInActive = this.getActiveActorIndexById(id);
        if (indexInActive >= 0) {
            console.log(id, 'active');
            const actor = this.activeActors[indexInActive];
            actor.nativeActor.stop();
            this.activeActors.splice(indexInActive, 1);
        }
        const indexInQueued = this.getQueuedActorIndexById(id);
        if (indexInQueued >= 0) {
            console.log(id, 'queue');
            this.queuedActors.splice(indexInQueued, 1);
        }
    }

    // INTERNAL
    // if there is activeQueue, and the no. of actors running is less than
    // the allowed value then start new actor
    updateActiveActors = () => {
        if (!this.inSession) {
            // current session has already completed
            // console.log('Session has already completed');
            return;
        }
        if (this.queuedActors.length <= 0 && this.activeActors.length <= 0) {
            // session has completed
            console.log('Session has completed');
            this.inSession = false;
            if (this.postSession) {
                this.postSession(this.totalErrors);
            }
            this.totalErrors = 0;
            return;
        }
        if (this.queuedActors.length <= 0) {
            // no queued actors left, but has active actors
            // console.log('No queued actors left');
            return;
        }
        if (this.activeActors.length >= this.maxActiveActors) {
            // over capacity
            // console.log('Update postponed until notified');
            return;
        }

        const toBeActiveActor = this.queuedActors[0];
        this.activeActors.push(toBeActiveActor);
        this.queuedActors.shift();

        // console.log('New actor started');

        // start actor
        toBeActiveActor.nativeActor.start();

        // call recurisively
        this.updateActiveActors();
    }

    // actors notify co-ordinator that it has completed with or without errors
    notifyComplete = (id, hasError = false) => {
        this.totalErrors += hasError ? 1 : 0;
        // remove from activeActors
        const actorIndex = this.getActiveActorIndexById(id);
        if (actorIndex < 0) {
            // console.warn('Id doesnt exist');
            return false;
        }
        this.activeActors.splice(actorIndex, 1);

        // console.log('Actor complete');
        // recalculate active actor list
        this.updateActiveActors();
        return true;
    }

    start = () => {
        this.inSession = true;
        if (this.preSession) {
            this.preSession();
        }
        this.updateActiveActors();
    }

    stop = () => {
        // console.warn('Session closing');
        const oldActiveActors = this.activeActors;

        // Clear everything
        this.activeActors = [];
        this.queuedActors = [];

        // stop current
        oldActiveActors.forEach(actor => actor.nativeActor.stop());

        /*
            // NOTE: don't call post session on stop
            // Make a different callback
            if (this.postSession) {
                this.postSession();
            }
        */
    }

    hasQueuedActors = () => this.queuedActors.length > 0;
}