text: const console = require('console');
  
  const {
      ServiceNotFoundError,
  } = require('./errors.js');
  
  const ServiceClient = require('./ServiceClient');
  
  /**
   * Basic class that represents the component and is exposed on the app.
   *
   * To get the services use `get(serviceName)` or `getService(serviceName)`
   *
   * @type {module.RemoteMicroservice}
   */
  module.exports = class RemoteMicroservice {
  
      constructor(app, settings, { logger = console } = {}) {
          this.app = app;
          this.settings = settings;
          this._services = this.createServiceClients(app, settings.services);
          this.logger = logger;
      }
  
      /**
       * @param {String} serviceName
       * @returns {ServiceClient}
       * @private
       */
      _getService(serviceName) {
          return this._services.get(serviceName);
      }
  
      /**
       * Alias for getService
       *
       * @see this.getService()
       * @param {String} serviceName
       * @return {Promise<ServiceClient>}
       */
      async get(serviceName, establishConnection = true) {
          return this.getService(serviceName, establishConnection);
      }
  
      /**
       * Access the service client for the service with the given serviceName
       *
       * @param {String} serviceName - name of the service
       * @param {Boolean} [establishConnection = true] - trigger the discovery or ensure connection
       * @return {Promise<ServiceClient>}
       */
      async getService(serviceName, establishConnection = true) {
  
          if (!this.hasService(serviceName)) {
              throw new ServiceNotFoundError(`No service with name "${serviceName}" registered.`);
          }
  
          const serviceClient = this._getService(serviceName);
  
          if (serviceClient.supportsDiscovery && establishConnection) {
              return serviceClient.discover();
          }
  
          if (serviceClient.supportsConnecting && establishConnection) {
              return serviceClient.connect();
          }
  
          return serviceClient;
      }
  
      /**
       * Checks if a service with a given name was configured.
       *
       * @param {String} serviceName
       * @return {Boolean}
       */
      hasService(serviceName) {
          return this._services.has(serviceName);
      }
  
      /**
       * Normalize hooks of the data source.
       *
       * Previously used to normalize the rest-connectors parameters, we will use this to propagate
       * settings which are not passed by loopback and might normalize the hooks.
       *
       * @param {DataSource} dataSource - a Loopback data source
       * @return {DataSource}
       */
      normalizeDataSource(dataSource) {
          // Assign the configuration option for backwards compatibility.
          // The correct (but undocumented) way of setting this option is to configure your data
          // source:
          // "data-source": {
          //   "options": {
          //     "rest": {
          //       "passAccessToken": true
          //     }
          //   }
          // }
          const passToken = dataSource.settings.passAccessToken === true;
          if (passToken === true) {
              // eslint-disable-next-line no-param-reassign
              dataSource.connector.remotes.serverAdapter.options.passAccessToken = true;
          }
          return dataSource;
      }
  
      /**
       * Creates a map containing service clients mapped to their serviceName.
       *
       * This also initializes the discovery if the service is configured to be auto-discovered.
       *
       * @param {ExpressApp} app - the app given by Loopback
       * @param {Object} serviceConfig - the service configuration given to the component
       * @return {Map<{String}, {ServiceClient}>}
       */
      createServiceClients(app, serviceConfig = {}) {
          return Object.values(serviceConfig)
              .reduce((serviceClients, conf) => {
                  // get the assigned data source
                  const dataSource = app.dataSources[conf.dataSource];
                  this.normalizeDataSource(dataSource);
                  // instantiate the client
                  const client = new ServiceClient(conf, dataSource);
                  // if the client is configured to be auto discovered, trigger the discovery
                  if (client.autoDiscoveryEnabled) {
                      client.discover().catch(error => this._handleDiscoveryError(error));
                  }
                  // map the client to the services name
                  serviceClients.set(client.serviceName, client);
                  return serviceClients;
              }, new Map());
      }
  
      /**
       * Logs errors generated by the serices auto discovery
       *
       * @param {Error} error
       * @private
       */
      _handleDiscoveryError(error) {
          this.logger.error(error);
      }
  };
  