<commit_before>import Url from 'url';

import {base64} from '@webex/common';

import WebexPlugin from '../webex-plugin';

import ServiceCatalog from './service-catalog';

/* eslint-disable no-underscore-dangle */
/**
 * @class
 */
const Services = WebexPlugin.extend({
  namespace: 'Services',

  props: {
    validateDomains: ['boolean', false, true]
  },

  _catalogs: new WeakMap(),

  /**
   * @private
   * Get the current catalog based on the assocaited
   * webex instance.
   * @returns {ServiceCatalog}
   */
  _getCatalog() {
    return this._catalogs.get(this.webex);
  },

  /**
   * Get a service url from the current services list by name
   * from the associated instance catalog.
   * @param {string} name
   * @param {boolean} [priorityHost]
   * @param {string} [serviceGroup]
   * @returns {string|undefined}
   */
  get(name, priorityHost, serviceGroup) {
    const catalog = this._getCatalog();

    return catalog.get(name, priorityHost, serviceGroup);
  },

  /**
   * Determine if the catalog contains a specific service
   *
   * @param {string} serviceName - The service name to validate.
   * @returns {boolean} - True if the service exists.
   */
  hasService(serviceName) {
    return !!(this.get(serviceName));
  },

  /**
   * Determine if a whilelist exists in the service catalog.
   *
   * @returns {boolean} - True if a allowed domains list exists.
   */
  hasAllowedDomains() {
    const catalog = this._getCatalog();

    return (catalog.getAllowedDomains().length > 0);
  },

  /**
   * Generate a service catalog as an object from
   * the associated instance catalog.
   * @param {boolean} [priorityHost] - use highest priority host if set to `true`
   * @param {string} [serviceGroup]
   * @returns {Record<string, string>}
   */
  list(priorityHost, serviceGroup) {
    const catalog = this._getCatalog();

    return catalog.list(priorityHost, serviceGroup);
  },

  /**
   * Mark a priority host service url as failed.
   * This will mark the host associated with the
   * `ServiceUrl` to be removed from the its
   * respective host array, and then return the next
   * viable host from the `ServiceUrls` host array,
   * or the `ServiceUrls` default url if no other priority
   * hosts are available, or if `noPriorityHosts` is set to
   * `true`.
   * @param {string} url
   * @param {boolean} noPriorityHosts
   * @returns {string}
   */
  markFailedUrl(url, noPriorityHosts) {
    const catalog = this._getCatalog();

    return catalog.markFailedUrl(url, noPriorityHosts);
  },

  /**
   * Update a list of `serviceUrls` to the most current
   * catalog via the defined `discoveryUrl` then returns the current
   * list of services.
   * @param {object} [param]
   * @param {string} [param.from] - This accepts `limited` or `signin`
   * @param {object} [param.query] - This accepts `email`, `orgId` or `userId` key values
   * @param {string} [param.query.email] - must be a standard-format email
   * @param {string} [param.query.orgId] - must be an organization id
   * @param {string} [param.query.userId] - must be a user id
   * @param {string} [param.token] - used for signin catalog
   * @returns {Promise<object>}
   */
  updateServices({from, query, token} = {}) {
    const catalog = this._getCatalog();
    let formattedQuery, serviceGroup;

    // map catalog name to service group name.
    switch (from) {
      case 'limited': serviceGroup = 'preauth'; break;
      case 'signin': serviceGroup = 'signin'; break;
      default: serviceGroup = 'postauth'; break;
    }

    // confirm catalog update for group is not in progress.
    if (catalog.status[serviceGroup].collecting) {
      return this.waitForCatalog(serviceGroup);
    }

    catalog.status[serviceGroup].collecting = true;

    if (serviceGroup === 'preauth') {
      const queryKey = query && Object.keys(query)[0];

      if (!['email', 'userId', 'orgId', 'mode'].includes(queryKey)) {
        return Promise.reject(new Error('a query param of email, userId, orgId, or mode is required'));
      }
    }
    // encode email when query key is email
    if (serviceGroup === 'preauth' || serviceGroup === 'signin') {
      const queryKey = Object.keys(query)[0];

      formattedQuery = {};
      formattedQuery[queryKey] = queryKey === 'email' ?
        base64.encode(query.email) : query[queryKey];
    }

    return this._fetchNewServiceHostmap({
      from,
      token,
      query: formattedQuery
    })
      .then((serviceHostMap) => {
        catalog.updateServiceUrls(serviceGroup, serviceHostMap);
        this.updateCredentialsConfig();
        catalog.status[serviceGroup].collecting = false;
      })
      .catch((error) => {
        catalog.status[serviceGroup].collecting = false;

        return Promise.reject(error);
      });
  },

  /**
   * User validation parameter transfer object for {@link validateUser}.
   * @param {object} ValidateUserPTO
   * @property {string} ValidateUserPTO.email - The email of the user.
   * @property {string} [ValidateUserPTO.reqId] - The activation requester.
   */

  /**
   * User validation return transfer object for {@link validateUser}.
   * @param {object} ValidateUserRTO
   * @property {boolean} ValidateUserRTO.activated - If the user is activated.
   * @property {boolean} ValidateUserRTO.exists - If the user exists.
   * @property {string} ValidateUserRTO.details - A descriptive status message.
   * @property {object} ValidateUserRTO.user - **Atlas** service user object.
   */

  /**
   * Validate if a user is activated and update the service catalogs as needed
   * based on the user's activation status.
   *
   * @param {ValidateUserPTO} - The parameter transfer object.
   * @returns {ValidateUserRTO} - The return transfer object.
   */
  validateUser({email, reqId = 'WEBCLIENT'}) {
    this.logger.info('services: validating a user');

    // Validate that an email parameter key was provided.
    if (!email) {
      return Promise.reject(new Error('`email` is required'));
    }

    // Destructure the credentials config object.
    const {canAuthorize} = this.webex.config;
<commit_msg>fix: read canAuthorize from credentials<commit_after>import Url from 'url';

import {base64} from '@webex/common';

import WebexPlugin from '../webex-plugin';

import ServiceCatalog from './service-catalog';

/* eslint-disable no-underscore-dangle */
/**
 * @class
 */
const Services = WebexPlugin.extend({
  namespace: 'Services',

  props: {
    validateDomains: ['boolean', false, true]
  },

  _catalogs: new WeakMap(),

  /**
   * @private
   * Get the current catalog based on the assocaited
   * webex instance.
   * @returns {ServiceCatalog}
   */
  _getCatalog() {
    return this._catalogs.get(this.webex);
  },

  /**
   * Get a service url from the current services list by name
   * from the associated instance catalog.
   * @param {string} name
   * @param {boolean} [priorityHost]
   * @param {string} [serviceGroup]
   * @returns {string|undefined}
   */
  get(name, priorityHost, serviceGroup) {
    const catalog = this._getCatalog();

    return catalog.get(name, priorityHost, serviceGroup);
  },

  /**
   * Determine if the catalog contains a specific service
   *
   * @param {string} serviceName - The service name to validate.
   * @returns {boolean} - True if the service exists.
   */
  hasService(serviceName) {
    return !!(this.get(serviceName));
  },

  /**
   * Determine if a whilelist exists in the service catalog.
   *
   * @returns {boolean} - True if a allowed domains list exists.
   */
  hasAllowedDomains() {
    const catalog = this._getCatalog();

    return (catalog.getAllowedDomains().length > 0);
  },

  /**
   * Generate a service catalog as an object from
   * the associated instance catalog.
   * @param {boolean} [priorityHost] - use highest priority host if set to `true`
   * @param {string} [serviceGroup]
   * @returns {Record<string, string>}
   */
  list(priorityHost, serviceGroup) {
    const catalog = this._getCatalog();

    return catalog.list(priorityHost, serviceGroup);
  },

  /**
   * Mark a priority host service url as failed.
   * This will mark the host associated with the
   * `ServiceUrl` to be removed from the its
   * respective host array, and then return the next
   * viable host from the `ServiceUrls` host array,
   * or the `ServiceUrls` default url if no other priority
   * hosts are available, or if `noPriorityHosts` is set to
   * `true`.
   * @param {string} url
   * @param {boolean} noPriorityHosts
   * @returns {string}
   */
  markFailedUrl(url, noPriorityHosts) {
    const catalog = this._getCatalog();

    return catalog.markFailedUrl(url, noPriorityHosts);
  },

  /**
   * Update a list of `serviceUrls` to the most current
   * catalog via the defined `discoveryUrl` then returns the current
   * list of services.
   * @param {object} [param]
   * @param {string} [param.from] - This accepts `limited` or `signin`
   * @param {object} [param.query] - This accepts `email`, `orgId` or `userId` key values
   * @param {string} [param.query.email] - must be a standard-format email
   * @param {string} [param.query.orgId] - must be an organization id
   * @param {string} [param.query.userId] - must be a user id
   * @param {string} [param.token] - used for signin catalog
   * @returns {Promise<object>}
   */
  updateServices({from, query, token} = {}) {
    const catalog = this._getCatalog();
    let formattedQuery, serviceGroup;

    // map catalog name to service group name.
    switch (from) {
      case 'limited': serviceGroup = 'preauth'; break;
      case 'signin': serviceGroup = 'signin'; break;
      default: serviceGroup = 'postauth'; break;
    }

    // confirm catalog update for group is not in progress.
    if (catalog.status[serviceGroup].collecting) {
      return this.waitForCatalog(serviceGroup);
    }

    catalog.status[serviceGroup].collecting = true;

    if (serviceGroup === 'preauth') {
      const queryKey = query && Object.keys(query)[0];

      if (!['email', 'userId', 'orgId', 'mode'].includes(queryKey)) {
        return Promise.reject(new Error('a query param of email, userId, orgId, or mode is required'));
      }
    }
    // encode email when query key is email
    if (serviceGroup === 'preauth' || serviceGroup === 'signin') {
      const queryKey = Object.keys(query)[0];

      formattedQuery = {};
      formattedQuery[queryKey] = queryKey === 'email' ?
        base64.encode(query.email) : query[queryKey];
    }

    return this._fetchNewServiceHostmap({
      from,
      token,
      query: formattedQuery
    })
      .then((serviceHostMap) => {
        catalog.updateServiceUrls(serviceGroup, serviceHostMap);
        this.updateCredentialsConfig();
        catalog.status[serviceGroup].collecting = false;
      })
      .catch((error) => {
        catalog.status[serviceGroup].collecting = false;

        return Promise.reject(error);
      });
  },

  /**
   * User validation parameter transfer object for {@link validateUser}.
   * @param {object} ValidateUserPTO
   * @property {string} ValidateUserPTO.email - The email of the user.
   * @property {string} [ValidateUserPTO.reqId] - The activation requester.
   */

  /**
   * User validation return transfer object for {@link validateUser}.
   * @param {object} ValidateUserRTO
   * @property {boolean} ValidateUserRTO.activated - If the user is activated.
   * @property {boolean} ValidateUserRTO.exists - If the user exists.
   * @property {string} ValidateUserRTO.details - A descriptive status message.
   * @property {object} ValidateUserRTO.user - **Atlas** service user object.
   */

  /**
   * Validate if a user is activated and update the service catalogs as needed
   * based on the user's activation status.
   *
   * @param {ValidateUserPTO} - The parameter transfer object.
   * @returns {ValidateUserRTO} - The return transfer object.
   */
  validateUser({email, reqId = 'WEBCLIENT'}) {
    this.logger.info('services: validating a user');

    // Validate that an email parameter key was provided.
    if (!email) {
      return Promise.reject(new Error('`email` is required'));
    }

    // Destructure the credentials object.
    const {canAuthorize} = this.webex.credentials;

{'fraction_non_alphanumeric': 0.1224640052356021, 'fraction_numerical': 0.0011452879581151832, 'mean_word_length': 3.155336505778382, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3140953', 'n_tokens_mistral': 3598, 'n_tokens_neox': 3575, 'n_words': 1119}