plugins/plugin_manager.js

const FormatReaders = require("./formats/read");
const FormatWriters = require("./formats/write");
const NetworkClient = require("./network");
const Repository = require("./repository");
const { Util } = require('../core/util');

/**
 * pluginTypes describe the types of DART plugins.
 *
 * - FormatReader reads files of a specific format. For example,
 *   TarReader reads tar files and FileSystemReader reads files directly
 *   from the file system.
 * - FormatWriter writes files in a specific format. For example,
 *   TarReader writes tar files and FileSystemReader writes files directly
 *   to the file system.
 * - NetworkClient sends (and possibly retrieves) files across a network.
 *   For example, S3Client can upload files to any service that implements
 *   the S3 REST API. FTPClient (when someone writes it) can upload files
 *   to an FTP server.
 * - Repository services can query different types of repositories. For
 *   example, an APTrust repository plugin can tell you the status of objects
 *   uploaded to APTrust. A Fedora plugin (if someone writes one) could do the
 *   same by talking to a Fedora REST service.
 *
 */
const pluginTypes = ['FormatReader', 'FormatWriter', 'NetworkClient', 'Repository'];

/**
 * PluginManager keeps track of available plugins and helps the DART core find
 * the plugins it needs to complete a job. For example, if a job requires that
 * we packaged a bag into a tar file and then send it via FTP to some remote
 * server, the plugin manager will find the plugins capable of writing tar files
 * and talking to FTP servers.
 *
 */
class PluginManager {

    /**
     * This returns a list of plugin types. E.g. 'FormatReader',
     * 'FormatWriter', etc.
     *
     * @returns {Array<string>}
     */
    static types() {
        return pluginTypes;
    }

    /**
     * This returns a list of available plugins of the specified
     * type. This function is primarily meant for internal use.
     *
     * @returns {Array<Plugin>}
     */
    static getModuleCollection(type) {
        var modules;
        switch (type) {
            case 'FormatReader':
              modules = FormatReaders.Providers;
              break;
            case 'FormatWriter':
              modules = FormatWriters.Providers;
              break;
            case 'NetworkClient':
              modules = NetworkClient.Providers;
              break;
            case 'Repository':
              modules = Repository.Providers;
              break;
            default:
              throw `Param 'type' must be one of: Array.join(pluginTypes, ', ')`;
        }
        return modules;
    }


    /**
     * This returns the plugin with the specified id.
     *
     * @returns {Plugin}
     */
    static findById(id) {
        for (var pluginType of pluginTypes) {
            var modules = PluginManager.getModuleCollection(pluginType);
            for (var module of modules) {
                if (module.description().id == id) {
                    return module;
                }
            }
        }
        return null;
    }

    /**
     * This returns a list of all plugins that can read the specified
     * file extension. For example, canRead('.tar') returns a list of
     * plugins that can read tar files. Use 'filesystem' if you're
     * looking for plugins that can read from the file system.
     *
     * @param {string} fileExtension - A string specifying the extension
     * of the file type you want to read. This should be all lowercase,
     * such as '.tar', '.zip', etc.
     *
     * @returns {Array<Plugin>}
     */
    static canRead(fileExtension) {
        return PluginManager.pluginProvides('FormatReader', 'readsFormats', fileExtension)
    }

    /**
     * This returns a list of all plugins that can write the specified
     * file extension. For example, canWrite('.tar') returns a list of
     * plugins that can write tar files. Use 'filesystem' if you're
     * looking for plugins that can write to the file system.
     *
     * @param {string} fileExtension - A string specifying the extension
     * of the file type you want to write. This should be all lowercase,
     * such as '.tar', '.zip', etc.
     *
     * @returns {Array<Plugin>}
     */
    static canWrite(fileExtension) {
        return PluginManager.pluginProvides('FormatWriter', 'writesFormats', fileExtension)
    }

    /**
     * This returns a list of all plugins that implement the specified
     * network protocol. For example, implementsProtocol('s3') returns a
     * list of plugins that can talk to s3 servers.
     *
     * @param {string} protocol - A string specifying what network protocol
     * you want to use. This should be all lowercase, such as 'ftp', 'sftp',
     * 's3', etc.
     *
     * @returns {Array<Plugin>}
     */
    static implementsProtocol(proto) {
        return PluginManager.pluginProvides('NetworkClient', 'implementsProtocols', proto)
    }

    /**
     * This returns a list of all plugins that can talk to repositories
     * of the specified type. For example, talksTo('fedora') returns a
     * list of plugins that can talk to Fedora REST services.
     *
     * @param {string} repoType - A string specifying what kind of repository
     * you want to talk to. This should be all lowercase, such as 'aptrust',
     * 'fedora', etc.
     *
     * @returns {Array<Plugin>}
     */
    static talksTo(repoType) {
        return PluginManager.pluginProvides('Repository', 'talksToRepository', repoType)
    }

    /**
     * This returns a list of plugins that provide a certain service,
     * such as being able to read a specified format or communicate via a
     * specific network protocol. This function is used internally by the
     * convenience functions {@link canRead}, {@link canWrite},
     * {@link implementsProtocol}, {@link talksTo}, and {@link setsUp}.
     * You should generally use those functions instead of this one.
     *
     * @returns {Array<Plugin>}
     */
    static pluginProvides(pluginType, propertyToCheck, valueToFind) {
        var modules = PluginManager.getModuleCollection(pluginType);
        var providers = [];
        for (var module of modules) {
            var desc = module.description();
            if (Array.isArray(desc[propertyToCheck]) && desc[propertyToCheck].includes(valueToFind)) {
                providers.push(module);
            } else if (desc[propertyToCheck] === valueToFind) {
                providers.push(module);
            }
        }
        return providers;
    }
}

module.exports.PluginManager = PluginManager;