Source: composer-common/lib/connectionprofilemanager.js

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

let LOG;

const connectionManagerLoaders = [];
const connectionManagerClasses = {};
const connectionManagers = {};

/**
 * A connection profile manager that manages a set of connection profiles. Each
 * connection profile defines an arbitrary set of configuration data and is associated
 * with a ConnectionManager.
 * @private
 * @class
 * @memberof module:composer-common
 */
class ConnectionProfileManager {

    /**
     * The composer-common module cannot load connector modules from parent modules
     * when the dependencies are linked together using npm link or lerna. To work
     * around this, the packages that require the connectors register themselves as
     * modules that can load connection managers.
     * @param {Object} module The module that can load connector modules.
     */
    static registerConnectionManagerLoader(module) {
        connectionManagerLoaders.push(module);
    }

    /**
     * Register a new ConnectionManager class.
     * @param {string} type - the profile type identifier of the ConnectionManager
     * @param {function} ctor - the constructor of the ConnectionManager
     */
    static registerConnectionManager(type, ctor) {
        connectionManagerClasses[type] = ctor;
    }

    /**
     * Create the ConnectionManager and attach a file system
     * @param {ConnectionProfileStore} connectionProfileStore - Node.js FS implementation, for example BrowserFS
     */
    constructor(connectionProfileStore) {
        if (!LOG) {
            LOG = require('./log/logger').getLog('ConnectionProfileManager');
        }
        LOG.info('constructor','Created a new ConnectionProfileManager', connectionProfileStore);

        if(!connectionProfileStore) {
            throw new Error('Must create ConnectionProfileManager with a ConnectionProfileStore implementation.');
        }

        this.connectionProfileStore = connectionProfileStore;
    }

    /**
     * Returns the ConnectionProfileStore associated with this
     * instance.
     * @return {ConnectionProfileStore} the associated store.
     */
    getConnectionProfileStore() {
        return this.connectionProfileStore;
    }

    /**
     * Adds a ConnectionManager to the mappings of this ConnectionProfileManager
     * @param {string} type - the profile type identifier of the ConnectionManager
     * @param {ConnectionManager} connectionManager - the instance
     */
    addConnectionManager(type, connectionManager) {
        LOG.info('addConnectionManager','Adding a new connection manager', type);
        connectionManagers[type] = connectionManager;
    }

    /**
     * Retrieves the ConnectionManager for the given connection profile.
     *
     * @param {string} connectionProfile The name of the connection profile
     * @return {Promise} A promise that is resolved with a {@link ConnectionManager}
     * object once the connection is established, or rejected with a connection error.
     */
    getConnectionManager(connectionProfile) {
        LOG.info('getConnectionManager','Looking up a connection manager for profile', connectionProfile);

        return this.connectionProfileStore.load(connectionProfile)
        .then((data) => {
            let connectionManager  = connectionManagers[data.type];
            if(!connectionManager) {
                const mod = `composer-connector-${data.type}`;
                try {
                    // Check for the connection manager class registered using
                    // registerConnectionManager (used by the web connector).
                    let connectionManagerClass = connectionManagerClasses[data.type];
                    if (connectionManagerClass) {
                        connectionManager = new(connectionManagerClass)(this);
                    } else {
                        // Not registered using registerConnectionManager, we now
                        // need to search for the connector module in our module
                        // and all of the parent modules (the ones who require'd
                        // us) as we do not depend on any connector modules.
                        let curmod = module;
                        while (curmod) {
                            try {
                                connectionManager = new(curmod.require(mod))(this);
                                break;
                            } catch (e) {
                                // Continue to search the parent.
                            }
                            curmod = curmod.parent;
                        }
                        if (!connectionManager) {
                            connectionManagerLoaders.some((connectionManagerLoader) => {
                                try {
                                    connectionManager = new(connectionManagerLoader.require(mod))(this);
                                    return true;
                                } catch (e) {
                                    // Search the next one.
                                    return false;
                                }
                            });
                        }
                        if (!connectionManager) {
                            // We still didn't find it, so try plain old require
                            // one last time.
                            connectionManager = new(require(mod))(this);
                        }
                    }
                } catch (e) {
                    throw new Error(`Failed to load connector module "${mod}" for connection profile "${connectionProfile}"`);
                }
                connectionManagers[data.type] = connectionManager;
            }
            return connectionManager;
        });
    }

    /**
     * Establish a connection to the business network, using connection information
     * from the connection profile.
     *
     * @param {string} connectionProfile The name of the connection profile
     * @param {string} businessNetworkIdentifier The identifier of the business network, or null if this is an admin connection
     * @return {Promise} A promise that is resolved with a {@link Connection}
     * object once the connection is established, or rejected with a connection error.
     * @abstract
     */
    connect(connectionProfile, businessNetworkIdentifier) {

        LOG.info('connect','Connecting using ' + connectionProfile, businessNetworkIdentifier);

        return this.connectionProfileStore.load(connectionProfile)
        .then((connectOptions) => {
            return this.getConnectionManager(connectionProfile)
          .then((connectionManager) => {
              return connectionManager.connect(connectionProfile, businessNetworkIdentifier, connectOptions);
          });
        });
    }

    /**
     * Stop serialization of this object.
     * @return {Object} An empty object.
     */
    toJSON() {
        return {};
    }

    /**
     * Clear the static object containing all the connection managers
     */
    static removeAllConnectionManagers() {
        connectionManagerLoaders.length = 0;
        Object.keys(connectionManagerClasses).forEach((key) => {
            connectionManagerClasses[key] = null;
        });
        Object.keys(connectionManagers).forEach((key) => {
            connectionManagers[key] = null;
        });
    }
}

module.exports = ConnectionProfileManager;