@AppKu/StashKu

@AppKu/StashKu

v1.0.46

source

engines/base-engine.js

import DeleteRequest from '../requests/delete-request.js';
import GetRequest from '../requests/get-request.js';
import OptionsRequest from '../requests/options-request.js';
import PatchRequest from '../requests/patch-request.js';
import PostRequest from '../requests/post-request.js';
import PutRequest from '../requests/put-request.js';
import RESTError from '../rest-error.js';
import Filter from '../filter.js';
import Logger from '../logger.js';

/**
 * @typedef
 */

/**
 * This abstract base class defines the structure of a StashKu-compatible engine. All StashKu engines must extend this
 * class.    
 * 
 * The implementing engine should implement it's own `schema`, `get`, `post`, `put`, `patch`, and `delete` functions
 * when supported. If the RESTful function is not supported it should not be overriden so that this base class can
 * throw a 501 "Not supported" error.
 * @abstract
 */
class BaseEngine {
    /**
     * Instantiates a new `BaseEngine`.
     * @param {String} name - The name of this storage engine. 
     */
    constructor(name) {
        if (new.target === BaseEngine) {
            throw new Error('BaseEngine is an abstract class and should not be initiated alone.');
        } else if (!name) {
            throw new Error('The "name" argument is required.');
        } else if (typeof name !== 'string') {
            throw new Error('The "name" argument must be a string.');
        }

        /**
         * The name of this StashKu engine. This name is used in debugging and log output.
         * @type {String}
         */
        this.name = name;

        /**
         * The configuration object for the storage engine.
         * @type {*}
         */
        this.config = null;

        /**
         * @type {Logger}
         */
        this.log = null;

    }

    /**
     * Standardized method to clean up resources within a storage engine. This is called from the StashKu instance when
     * an engine is replaced or removed, or the instance is destroyed.
     * @abstract
     */
    async destroy() { }

    /**
     * Sets the storage engine configuration property. This function is called automatically by StashKu when the engine
     * is initialized or when the configuration source has been changed.
     * @param {*} config - The configuration object for the storage engine.
     * @param {Logger} [logger] - Optional `Logger` instance to set on the engine. This is usually assigned to the
     * same instance as the StashKu parent.
     * @abstract
     */
    configure(config, logger) {
        this.config = config;
        if (logger) {
            this.log = logger;
        } else if (logger === null) {
            this.log = null;
        }
    }

    /**
     * Returns an array of strings naming the resources available in the storage medium accessible by the engine.
     * @returns {Promise.<Array.<String>>}
     * @abstract
     */
    async resources() {
        throw new RESTError(501, `The "resources" function is not supported on the StashKu "${this.name}" storage engine.`);
    }

    /**
     * Run a GET `request` and returns objects from storage that match the GET `request` criteria.
     * 
     * @throws A 501 REST error if not overriden and supported by the storage engine.
     * @throws A 400 REST error if the request is missing or not a `GetRequest`.
     * @throws A 400 REST error if the request missing required metadata.
     * @throws A 400 REST error if the request metadata is missing a required "from" value.
     * @param {GetRequest} request - The GET request to send to the storage engine.
     * @returns {Promise.<Response>} Returns the data objects from storage matching request criteria.
     * @abstract
     */
    async get(request) {
        if (this.get === BaseEngine.prototype.get) {
            throw new RESTError(501, `The GET action is not supported on the StashKu "${this.name}" storage engine.`);
        } else {
            //perform request validations
            if (!request || (request instanceof GetRequest) === false) {
                throw new RESTError(400, 'The "request" argument is required and must be a GetRequest.');
            } else if (!request.metadata) {
                throw new RESTError(400, 'The "request" argument is incomplete and missing required metadata.');
            } else if (!request.metadata.from) {
                throw new RESTError(400, 'The request is missing a required "from" value.');
            }
        }
    }

    /**
     * Run a POST `request` and creates (then returns) objects in storage.
     * 
     * @throws A 501 REST error if not overriden and supported by the storage engine.
     * @throws A 400 REST error if the request is missing or not a `PostRequest`.
     * @throws A 400 REST error if the request missing required metadata.
     * @throws A 400 REST error if the request metadata is missing a required "to" value.
     * @param {PostRequest} request - The POST request to send to the storage engine.
     * @returns {Promise.<Response>} Returns the data objects from storage that were created with the request criteria.
     * @abstract
     */
    async post(request) {
        if (this.post === BaseEngine.prototype.post) {
            throw new RESTError(501, `The POST action is not supported on the StashKu "${this.name}" storage engine.`);
        } else {
            //perform request validations
            if (!request || (request instanceof PostRequest) === false) {
                throw new RESTError(400, 'The "request" argument is required and must be a PostRequest.');
            } else if (!request.metadata) {
                throw new RESTError(400, 'The "request" argument is incomplete and missing required metadata.');
            } else if (!request.metadata.to) {
                throw new RESTError(400, 'The request is missing a required "to" value.');
            }
        }
    }

    /**
     * Run a PUT `request` and updates (then returns) objects in storage.
     * 
     * @throws A 501 REST error if not overriden and supported by the storage engine.
     * @throws A 400 REST error if the request is missing or not a `PutRequest`.
     * @throws A 400 REST error if the request missing required metadata.
     * @throws A 400 REST error if the request metadata is missing a required "to" value.
     * @throws A 400 REST error if the request metadata is missing at least one "pk" value.
     * @param {PutRequest} request - The PUT request to send to the storage engine.
     * @returns {Promise.<Response>} Returns the data objects from storage that were updated with the request criteria. This 
     * *__may not__* exactly match the objects requested to be updated, as some may have been deleted from storage or
     * some may not match the key criteria.
     * @abstract
     */
    async put(request) {
        if (this.put === BaseEngine.prototype.put) {
            throw new RESTError(501, `The PUT action is not supported on the StashKu "${this.name}" storage engine.`);
        } else {
            //perform request validations
            if (!request || (request instanceof PutRequest) === false) {
                throw new RESTError(400, 'The "request" argument is required and must be a PutRequest.');
            } else if (!request.metadata) {
                throw new RESTError(400, 'The "request" argument is incomplete and missing required metadata.');
            } else if (!request.metadata.to) {
                throw new RESTError(400, 'The request is missing a required "to" value.');
            } else if (!request.metadata.pk || request.metadata.pk.length === 0) {
                throw new RESTError(400, 'The request is missing at least one "primary key" (PK) property name.');
            }
        }
    }
    /**
     * Run a PATCH `request` and updates all objects in storage matching the PATCH `request` criteria. A count of the
     * number of objects affected is returned.
     * 
     * @throws A 501 REST error if not overriden and supported by the storage engine.
     * @throws A 400 REST error if the request is missing or not a `PatchRequest`.
     * @throws A 400 REST error if the request missing required metadata.
     * @throws A 400 REST error if the request metadata is missing a required "to" value.
     * @throws A 400 REST error if the request metadata is missing a "template" object.
     * @throws A 400 REST error if the request metadata is missing "where" conditions to match objects in storage and
     * is not enabled to affect all objects.
     * @param {PatchRequest} request - The PATCH request to send to the storage engine.
     * @returns {Promise.<Response>} Returns a response with the total number of the objects affected in storage. No
     * data objects are typically returned with this request.
     * @abstract
     */
    async patch(request) {
        if (this.patch === BaseEngine.prototype.patch) {
            throw new RESTError(501, `The PATCH action is not supported on the StashKu "${this.name}" storage engine.`);
        } else {
            //perform request validations
            if (!request || (request instanceof PatchRequest) === false) {
                throw new RESTError(400, 'The "request" argument is required and must be a PatchRequest.');
            } else if (!request.metadata) {
                throw new RESTError(400, 'The "request" argument is incomplete and missing required metadata.');
            } else if (!request.metadata.to) {
                throw new RESTError(400, 'The request is missing a required "to" value.');
            } else if (!request.metadata.template) {
                throw new RESTError(400, 'The request is missing at a defined "template" object.');
            } else if (!request.metadata.all && (!request.metadata.where || Filter.isEmpty(request.metadata.where))) {
                throw new RESTError(400, 'The request is missing "where" conditions to match objects in storage. If the intention is to affect all objects, the "all" flag must be enabled.');
            }
        }
    }

    /**
     * Run a DELETE `request` and deletes all objects in storage matching the DELETE `request` criteria.
     * 
     * @throws A 501 REST error if not overriden and supported by the storage engine.
     * @throws A 400 REST error if the request is missing or not a `DeleteRequest`.
     * @throws A 400 REST error if the request missing required metadata.
     * @throws A 400 REST error if the request metadata is missing a required "from" value.
     * @throws A 400 REST error if the request metadata is missing "where" conditions to match objects in storage and
     * is not enabled to affect all objects.
     * @param {DeleteRequest} request - The DELETE request to send to the storage engine.
     * @returns {Promise.<Response>} Returns the data objects from storage that were deleted with the request criteria.
     * @abstract
     */
    async delete(request) {
        if (this.delete === BaseEngine.prototype.delete) {
            throw new RESTError(501, `The DELETE action is not supported on the StashKu "${this.name}" storage engine.`);
        } else {
            //perform request validations
            if (!request || (request instanceof DeleteRequest) === false) {
                throw new RESTError(400, 'The "request" argument is required and must be a DeleteRequest.');
            } else if (!request.metadata) {
                throw new RESTError(400, 'The "request" argument is incomplete and missing required metadata.');
            } else if (!request.metadata.from) {
                throw new RESTError(400, 'The request is missing a required "from" value.');
            } else if (!request.metadata.all && (!request.metadata.where || Filter.isEmpty(request.metadata.where))) {
                throw new RESTError(400, 'The request is missing "where" conditions to match objects in storage and is not enabled to affect all objects.');
            }
        }
    }

    /**
     * Run an OPTIONS `request` which returns a dynamically constructed model type which defines how StashKu can 
     * interact with the target (`from`) resource. 
     * 
     * @throws A 501 REST error if not overriden and supported by the storage engine.
     * @throws A 400 REST error if the request is missing or not a `OptionsRequest`.
     * @throws A 400 REST error if the request missing required metadata.
     * @throws A 400 REST error if the request metadata is missing a required "from" value.
     * @throws A 400 REST error if the request metadata is missing "where" conditions to match objects in storage and
     * is not enabled to affect all objects.
     * @param {OptionsRequest} request - The OPTIONS request to send to the storage engine.
     * @returns {Promise.<Response>} Returns a response with a single data object- the dynamically created model
     * configuration.
     * @abstract
     */
    async options(request) {
        if (this.options === BaseEngine.prototype.options) {
            throw new RESTError(501, `The OPTIONS action is not supported on the StashKu "${this.name}" storage engine.`);
        } else {
            //perform request validations
            if (!request || (request instanceof OptionsRequest) === false) {
                throw new RESTError(400, 'The "request" argument is required and must be a OptionsRequest.');
            } else if (!request.metadata) {
                throw new RESTError(400, 'The "request" argument is incomplete and missing required metadata.');
            } else if (!request.metadata.from) {
                throw new RESTError(400, 'The request is missing a required "from" value.');
            }
        }
    }

}

export default BaseEngine;