text: /**
   * Converts the standard Strapi REST query params to a moe usable format for querying
   * You can read more here: https://strapi.io/documentation/3.0.0-beta.x/guides/filters.html
   */
  
  const _ = require('lodash');
  const {
    constants: { DP_PUB_STATES },
  } = require('./content-types');
  
  const BOOLEAN_OPERATORS = ['or'];
  const QUERY_OPERATORS = ['_where', '_or'];
  
  /**
   * Global converter
   * @param {Object} params
   * @param defaults
   */
  const convertRestQueryParams = (params = {}, defaults = {}) => {
    if (typeof params !== 'object' || params === null) {
      throw new Error(
        `convertRestQueryParams expected an object got ${params === null ? 'null' : typeof params}`
      );
    }
  
    let finalParams = Object.assign({ start: 0, limit: 100 }, defaults);
  
    if (Object.keys(params).length === 0) {
      return finalParams;
    }
  
    if (_.has(params, '_sort')) {
      Object.assign(finalParams, convertSortQueryParams(params._sort));
    }
  
    if (_.has(params, '_start')) {
      Object.assign(finalParams, convertStartQueryParams(params._start));
    }
  
    if (_.has(params, '_limit')) {
      Object.assign(finalParams, convertLimitQueryParams(params._limit));
    }
  
    if (_.has(params, '_publicationState')) {
      Object.assign(finalParams, convertPublicationStateParams(params._publicationState));
    }
  
    const whereParams = _.omit(params, ['_sort', '_start', '_limit', '_where', '_publicationState']);
    const whereClauses = [];
  
    if (_.keys(whereParams).length > 0) {
      whereClauses.push(...convertWhereParams(whereParams));
    }
  
    if (_.has(params, '_where')) {
      whereClauses.push(...convertWhereParams(params._where));
    }
  
    Object.assign(finalParams, { where: whereClauses });
  
    return finalParams;
  };
  
  /**
   * Sort query parser
   * @param {string} sortQuery - ex: id:asc,price:desc
   */
  const convertSortQueryParams = sortQuery => {
    if (typeof sortQuery !== 'string') {
      throw new Error(`convertSortQueryParams expected a string, got ${typeof sortQuery}`);
    }
  
    const sortKeys = [];
  
    sortQuery.split(',').forEach(part => {
      // split field and order param with default order to ascending
      const [field, order = 'asc'] = part.split(':');
  
      if (field.length === 0) {
        throw new Error('Field cannot be empty');
      }
  
      if (!['asc', 'desc'].includes(order.toLocaleLowerCase())) {
        throw new Error('order can only be one of asc|desc|ASC|DESC');
      }
  
      sortKeys.push({ field, order: order.toLowerCase() });
    });
  
    return {
      sort: sortKeys,
    };
  };
  
  /**
   * Start query parser
   * @param {string} startQuery - ex: id:asc,price:desc
   */
  const convertStartQueryParams = startQuery => {
    const startAsANumber = _.toNumber(startQuery);
  
    if (!_.isInteger(startAsANumber) || startAsANumber < 0) {
      throw new Error(`convertStartQueryParams expected a positive integer got ${startAsANumber}`);
    }
  
    return {
      start: startAsANumber,
    };
  };
  
  /**
   * Limit query parser
   * @param {string} limitQuery - ex: id:asc,price:desc
   */
  const convertLimitQueryParams = limitQuery => {
    const limitAsANumber = _.toNumber(limitQuery);
  
    if (!_.isInteger(limitAsANumber) || (limitAsANumber !== -1 && limitAsANumber < 0)) {
      throw new Error(`convertLimitQueryParams expected a positive integer got ${limitAsANumber}`);
    }
  
    return {
      limit: limitAsANumber,
    };
  };
  
  /**
   * publicationState query parser
   * @param {string} publicationState - eg: 'live', 'preview'
   */
  const convertPublicationStateParams = publicationState => {
    if (!DP_PUB_STATES.includes(publicationState)) {
      throw new Error(
        `convertPublicationStateParams expected a value from: ${DP_PUB_STATES.join(
          ', '
        )}. Got ${publicationState} instead`
      );
    }
  
    return { publicationState };
  };
  
  // List of all the possible filters
  const VALID_REST_OPERATORS = [
    'eq',
    'ne',
    'in',
    'nin',
    'contains',
    'ncontains',
    'containss',
    'ncontainss',
    'lt',
    'lte',
    'gt',
    'gte',
    'null',
  ];
  
  /**
   * Parse where params
   */
  const convertWhereParams = whereParams => {
    let finalWhere = [];
  
    if (Array.isArray(whereParams)) {
      return whereParams.reduce((acc, whereParam) => {
        return acc.concat(convertWhereParams(whereParam));
      }, []);
    }
  
    Object.keys(whereParams).forEach(whereClause => {
      const { field, operator = 'eq', value } = convertWhereClause(
        whereClause,
        whereParams[whereClause]
      );
  
      finalWhere.push({
        field,
        operator,
        value,
      });
    });
  
    return finalWhere;
  };
  
  /**
   * Parse single where param
   * @param {string} whereClause - Any possible where clause e.g: id_ne text_ncontains
   * @param {string} value - the value of the where clause e.g id_ne=value
   */
  const convertWhereClause = (whereClause, value) => {
    const separatorIndex = whereClause.lastIndexOf('_');
  
    // eq operator
    if (separatorIndex === -1) {
      return { field: whereClause, value };
    }
  
    // split field and operator
    const field = whereClause.substring(0, separatorIndex);
    const operator = whereClause.slice(separatorIndex + 1);
  
    if (BOOLEAN_OPERATORS.includes(operator) && field === '') {
      return { field: null, operator, value: [].concat(value).map(convertWhereParams) };
    }
  
    // the field as underscores
    if (!VALID_REST_OPERATORS.includes(operator)) {
      return { field: whereClause, value };
    }
  
    return { field, operator, value };
  };
  
  module.exports = {
    convertRestQueryParams,
    VALID_REST_OPERATORS,
    QUERY_OPERATORS,
  };
  