daemonraco/dfdb

View on GitHub
src/includes/connection/file.sl.dfdb.ts

Summary

Maintainability
A
1 hr
Test Coverage
/**
 * @file file.sl.dfdb.ts
 * @author Alejandro D. Simi
 */

import { Promise } from 'es6-promise';
import { queue } from 'async';
import * as fs from 'fs';

import { ConnectionSaveConstants } from '../constants.dfdb';
import { ConnectionSavingQueueResult } from './types.dfdb';
import { IOpenConnectionFile } from './open-connection.i.dfdb';
import { Rejection } from '../rejection.dfdb';
import { RejectionCodes } from '../rejection-codes.dfdb';
import { SubLogic } from '../sub-logic.dfdb';

/**
 * This class holds Connection's specific logic to manpulate a database files.
 *
 * @class SubLogicFile
 */
export class SubLogicFile extends SubLogic<IOpenConnectionFile> {
    /**
     * This method centralizes all calls to load a file from inside the database
     * zip file.
     *
     * @method loadFile
     * @param {string} zPath Internal path to load.
     * @returns {Promise<ConnectionSavingQueueResult>} Returns a standarized
     * result object.
     */
    public loadFile(zPath: string): Promise<ConnectionSavingQueueResult> {
        //
        // Building promise to return.
        return new Promise<ConnectionSavingQueueResult>((resolve: (res: ConnectionSavingQueueResult) => void, reject: (err: Rejection) => void) => {
            //
            // is it connected?
            if (this._mainObject._connected) {
                //
                // Piling up a new zip access operation to read a file.
                this._mainObject._fileAccessQueue.push({
                    action: ConnectionSaveConstants.LoadFile,
                    path: zPath
                }, (result: ConnectionSavingQueueResult) => resolve(result));
            } else {
                //
                // It should be connected to actually save.
                this._mainObject._subLogicErrors.setLastRejection(new Rejection(RejectionCodes.DatabaseNotConnected));
                reject(this._mainObject._subLogicErrors.lastRejection());
            }
        });
    }
    /**
     * This method actually saves zip information physically.
     *
     * @method save
     * @returns {Promise<void>} Returns a promise that gets resovled when this
     * operation is finished.
     */
    public save(): Promise<void> {
        //
        // Restarting error messages.
        this._mainObject._subLogicErrors.resetError();
        //
        // Building promise to return.
        return new Promise<void>((resolve: () => void, reject: (err: Rejection) => void) => {
            //
            // Is it connected?
            if (this._mainObject._connected) {
                //
                // Updating internal manifest file's contents. If it doesn't
                // exist, it is created.
                this._mainObject._dbFile.file(this._mainObject._manifestPath, JSON.stringify(this._mainObject._manifest));
                //
                // Physcally saving.
                this._mainObject._dbFile
                    .generateNodeStream({ type: 'nodebuffer', streamFiles: true })
                    .pipe(fs.createWriteStream(this._mainObject._dbFullPath))
                    .on('finish', resolve);
            } else {
                //
                // It should be connected to actually save.
                this._mainObject._subLogicErrors.setLastRejection(new Rejection(RejectionCodes.DatabaseNotConnected));
                reject(this._mainObject._subLogicErrors.lastRejection());
            }
        });
    }
    /**
     * This method centralizes all calls to remove a file from inside the database
     * zip file.
     *
     * @method removeFile
     * @param {string} zPath Internal path to remove.
     * @returns {Promise<ConnectionSavingQueueResult>} Returns a standarized
     * result object.
     */
    public removeFile(zPath: string): Promise<ConnectionSavingQueueResult> {
        //
        // Building promise to return.
        return new Promise<ConnectionSavingQueueResult>((resolve: (res: ConnectionSavingQueueResult) => void, reject: (err: Rejection) => void) => {
            //
            // Is it connected?
            if (this._mainObject._connected) {
                //
                // Piling up a new zip access operation to remove a file.
                this._mainObject._fileAccessQueue.push({
                    action: ConnectionSaveConstants.RemoveFile,
                    path: zPath
                }, (results: ConnectionSavingQueueResult) => resolve(results));
            } else {
                //
                // It should be connected to actually save.
                this._mainObject._subLogicErrors.setLastRejection(new Rejection(RejectionCodes.DatabaseNotConnected));
                reject(this._mainObject._subLogicErrors.lastRejection());
            }
        });
    }
    /**
     * This method creates a queue to centralize all zip file access.
     *
     * @method setSavingQueue
     */
    public setFileAccessQueue(): void {
        //
        // Creating a new queue.
        // @note It only allow 1 (one) access at a time.
        this._mainObject._fileAccessQueue = queue((task: any, next: (res: ConnectionSavingQueueResult) => void) => {
            //
            // Default values.
            const resutls: ConnectionSavingQueueResult = new ConnectionSavingQueueResult();
            //
            // What action should it attend?
            switch (task.action) {
                case ConnectionSaveConstants.LoadFile:
                    //
                    // Retrieving file from zip.
                    const file = this._mainObject._dbFile.file(task.path);
                    //
                    // Does it exist?
                    if (file !== null) {
                        //
                        // Parsing and returning its connents.
                        file.async('text').then((data: string) => {
                            resutls.data = data;
                            next(resutls);
                        });
                    } else {
                        //
                        // Setting an error message and responding.
                        resutls.error = `Zip path '${task.path}' does not exist`;
                        next(resutls);
                    }
                    break;
                case ConnectionSaveConstants.RemoveFile:
                    //
                    // Removing a file from zip.
                    this._mainObject._dbFile.remove(task.path);
                    next(resutls);
                    break;
                case ConnectionSaveConstants.UpdateFile:
                    //
                    // Updating file's contents. If it doesn't exist, it is
                    // created.
                    this._mainObject._dbFile.file(task.path, task.data);
                    //
                    // Should the physical file be updated?
                    if (!task.skipPhysicalSave) {
                        this._mainObject.save()
                            .then(() => {
                                next(resutls);
                            })
                            .catch((err: string) => next(resutls));
                    } else {
                        next(resutls);
                    }
                    break;
                default:
                    //
                    // At this point, the requested action can't be attended.
                    resutls.error = `Unknown action '${task.action}'`;
                    next(resutls);
            }
        }, 1);
    }
    /**
     * This method centralizes all calls to update contents of a file inside the
     * database zip file.
     *
     * @method updateFile
     * @param {string} zPath Internal path to remove.
     * @param {any} data Information to be stored.
     * @param {boolean} skipPhysicalSave After virtually updating the file, should
     * physical storage be updated?
     * @returns {Promise<ConnectionSavingQueueResult>} Returns a standarized
     * result object.
     */
    public updateFile(zPath: string, data: any, skipPhysicalSave: boolean = false): Promise<ConnectionSavingQueueResult> {
        //
        // Building promise to return.
        return new Promise<ConnectionSavingQueueResult>((resolve: (res: ConnectionSavingQueueResult) => void, reject: (err: Rejection) => void) => {
            //
            // Is it connected?
            if (this._mainObject._connected) {
                this._mainObject._fileAccessQueue.push({
                    action: ConnectionSaveConstants.UpdateFile,
                    path: zPath,
                    data, skipPhysicalSave
                }, (results: ConnectionSavingQueueResult) => resolve(results));
            } else {
                //
                // It should be connected to actually save.
                this._mainObject._subLogicErrors.setLastRejection(new Rejection(RejectionCodes.DatabaseNotConnected));
                reject(this._mainObject._subLogicErrors.lastRejection());
            }
        });
    }
}