daemonraco/dfdb

View on GitHub
src/includes/sequence.dfdb.ts

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * @file sequence.dfdb.ts
 * @author Alejandro D. Simi
 */

import { Promise } from 'es6-promise';
import * as JSZip from 'jszip';

import { Collection } from './collection/collection.dfdb';
import { Connection, ConnectionSavingQueueResult } from './connection/connection.dfdb';
import { IDelayedResource, IResource } from './resource.i.dfdb';
import { IErrors } from './errors.i.dfdb';
import { Rejection } from './rejection.dfdb';
import { RejectionCodes } from './rejection-codes.dfdb';
import { SubLogicErrors } from './errors.sl.dfdb';

/**
 * This class represents a sequence of ids associated to a collection.
 *
 * @class Sequence
 */
export class Sequence implements IErrors, IResource, IDelayedResource {
    //
    // Protected properties.
    protected _collection: Collection = null;
    protected _connected: boolean = false;
    protected _connection: Connection = null;
    protected _name: string = null;
    protected _resourcePath: string = null;
    protected _skipSave: boolean = false;
    protected _subLogicErrors: SubLogicErrors<Sequence> = null;
    protected _value: number = 0;
    //
    // Constructor.
    /**
     * @constructor
     * @param {Collection} collection Collection to which this index is
     * associated.
     * @param {Connection} connection Collection in which it's stored.
     */
    constructor(collection: Collection, name: string, connection: Connection) {
        //
        // Shortcuts.
        this._name = name;
        this._collection = collection;
        this._connection = connection;
        //
        // Main path.
        this._resourcePath = `${this._collection.name()}/${this._name}.seq`;
        //
        // Sub-logics.
        this._subLogicErrors = new SubLogicErrors<Sequence>(this);
    }
    //
    // Public methods.
    /**
     * Creating a sequence object doesn't mean it is connected to physical
     * information, this method does that.
     * It connects and loads information from the physical storage in zip file.
     *
     * @method connect
     * @returns {Promise<void>} Return a promise that gets resolved when the
     * operation finishes.
     */
    public connect(): Promise<void> {
        //
        // Restarting error messages.
        this._subLogicErrors.resetError();
        //
        // Building promise to return.
        return new Promise<void>((resolve: () => void, reject: (err: Rejection) => void) => {
            //
            // Is it connected?
            if (!this._connected) {
                //
                // Retrieving data from zip file.
                this._connection.loadFile(this._resourcePath)
                    .then((results: ConnectionSavingQueueResult) => {
                        //
                        // Did it fail?
                        if (results.error) {
                            //
                            // Setting as connected.
                            this._connected = true;
                            //
                            // Starting from scratch.
                            this._value = 0;
                            //
                            // Forcing to save... skipping the skip save :)
                            this._skipSave = false;
                            //
                            // Physically saving data.
                            this.save()
                                .then(resolve)
                                .catch(reject);
                        } else if (results.data !== null) {
                            //
                            // Parsing data.
                            this._value = parseInt(results.data);
                            //
                            // At this point, this secuence is considered to be
                            // connected.
                            this._connected = true;
                            resolve();
                        }
                    })
                    .catch(reject);
            } else {
                //
                // If it's already connected, nothing is done.
                resolve();
            }
        });
    }
    /**
     * This method saves current value and then closes this sequence.
     *
     * @method close
     * @returns {Promise<void>} Return a promise that gets resolved when the
     * operation finishes.
     */
    public close(): Promise<void> {
        //
        // Restarting error messages.
        this._subLogicErrors.resetError();
        //
        // Building promise to return.
        return new Promise<void>((resolve: () => void, reject: (err: Rejection) => void) => {
            //
            // Is it connected?
            if (this._connected) {
                //
                // Saving value.
                this.save()
                    .then(() => {
                        //
                        // Resetting current value.
                        this._value = 0;
                        //
                        // Disconnecting this sequence to avoid issues.
                        this._connected = false;

                        resolve();
                    })
                    .catch(reject);
            } else {
                //
                // If it's not connected, nothing is done.
                resolve();
            }
        });
    }
    /**
     * This method removes this sequence from its connection and erases all
     * traces of it.
     *
     * @method drop
     * @returns {Promise<void>} Return a promise that gets resolved when the
     * operation finishes.
     */
    public drop(): Promise<void> {
        //
        // Restarting error messages.
        this._subLogicErrors.resetError();
        //
        // Building promise to return.
        return new Promise<void>((resolve: () => void, reject: (err: Rejection) => void) => {
            //
            // Is it connected?
            if (this._connected) {
                //
                // Removing file from zip.
                this._connection.removeFile(this._resourcePath)
                    .then((results: ConnectionSavingQueueResult) => {
                        // no need to ask collection to forget this index because it's the
                        // collection's responsibillity to invoke this method and then
                        // forget it.

                        this._connected = false;
                        resolve();
                    })
                    .catch(reject);
            } else {
                //
                // If it's not connected, nothing is done.
                resolve();
            }
        });
    }
    /**
     * Provides a way to know if there was an error in the last operation.
     *
     * @method error
     * @returns {boolean} Returns TRUE when there was an error.
     */
    public error(): boolean {
        //
        // Forwarding to sub-logic.
        return this._subLogicErrors.error();
    }
    /**
     * Provides access to the error message registed by the last operation.
     *
     * @method lastError
     * @returns {string|null} Returns an error message.
     */
    public lastError(): string | null {
        //
        // Forwarding to sub-logic.
        return this._subLogicErrors.lastError();
    }
    /**
     * Provides access to the rejection registed by the last operation.
     *
     * @method lastRejection
     * @returns {Rejection} Returns an rejection object.
     */
    public lastRejection(): Rejection {
        //
        // Forwarding to sub-logic.
        return this._subLogicErrors.lastRejection();
    }
    /**
     * This method advances the internal counter and returns it for ID usage
     * guaranteeing that it's unique.
     *
     * @method next
     * @returns {string} Returns a unique ID.
     */
    public next(): string {
        //
        // Restarting error messages.
        this._subLogicErrors.resetError();
        //
        // Incrementing internal counter.
        this._value++;
        //
        // Committing new value to file.
        this.save()
            .then(() => { })
            .catch(() => { });
        //
        // Returning the ID and enforsing that it's a string.
        return `${this._value}`;
    }
    /**
     * When the physical file saving is trigger by a later action, this method
     * avoids next file save attempt for this sequence.
     *
     * @method skipSave
     */
    public skipSave(): void {
        this._skipSave = true;
    }
    /**
     * This methods provides a proper value for string auto-castings.
     *
     * @method toString
     * @returns {string} Returns a simple string identifying this sequence.
     */
    public toString = (): string => {
        return `sequence:${this._name}`;
    }
    //
    // Protected methods.
    /**
     * This method triggers the physical saving of this sequence file.
     *
     * @protected
     * @method save
     * @returns {Promise<void>} Return a promise that gets resolved when the
     * operation finishes.
     */
    protected save(): Promise<void> {
        //
        // Building promise to return.
        return new Promise<void>((resolve: () => void, reject: (err: Rejection) => void) => {
            //
            // Saving file.
            this._connection.updateFile(this._resourcePath, `${this._value}`, this._skipSave)
                .then((results: ConnectionSavingQueueResult) => {
                    //
                    // Skipped once.
                    this._skipSave = false;

                    resolve();
                })
                .catch(reject);
        });
    }
}