<commit_before>/* eslint-disable no-use-before-define */

import {
  getNamedType,
  GraphQLInputType,
  GraphQLInputObjectType,
  GraphQLScalarType,
  GraphQLEnumType,
} from 'graphql-compose/lib/graphql';
import type { Model, Schema, SchemaType, VirtualType } from 'mongoose';
import type { InputTypeComposer } from 'graphql-compose';
import type { InputTypeComposerFieldConfigAsObjectDefinition } from 'graphql-compose';

import { upperFirst } from '../../utils';
import type { FilterHelperArgsOpts } from './filter';
import GraphQLRegExpAsString from '../../types/RegExpAsString';

export type FilterOperatorNames =
  | 'gt'
  | 'gte'
  | 'lt'
  | 'lte'
  | 'ne'
  | 'in[]'
  | 'nin[]'
  | 'regex'
  | 'exists';

export enum availableOperators {
  GT = 'gt',
  GTE = 'gte',
  LT = 'lt',
  LTE = 'lte',
  NE = 'ne',
  IN = 'in',
  NIN = 'nin',
  REGEX = 'regex',
  EXISTS = 'exists',
}

export type FilterOperatorsOpts =
  | {
      [fieldName: string]: FilterOperatorsOpts | FilterOperatorNames[] | false;
    }
  | false;

export const OPERATORS_FIELDNAME = '_operators';

export function addFilterOperators(
  itc: InputTypeComposer<any>,
  model: Model<any>,
  opts: FilterHelperArgsOpts
): void {
  if (!{}.hasOwnProperty.call(opts, 'operators') || opts.operators !== false) {
    _createOperatorsField(
      itc,
      `Operators${itc.getTypeName() || ''}`,
      model,
      opts.operators || false,
      opts.onlyIndexed
    );
  }

  itc.addFields({
    OR: [itc.getTypeNonNull()],
    AND: [itc.getTypeNonNull()],
  });
}

export function _availableOperatorsFields(
  fieldName: string,
  itc: InputTypeComposer,
  operatorsConfig: any
): Record<string, InputTypeComposerFieldConfigAsObjectDefinition> {
  const fields: Record<string, InputTypeComposerFieldConfigAsObjectDefinition> = {};

  const operators = Array.isArray(operatorsConfig)
    ? operatorsConfig.filter((value) => Object.values(availableOperators).includes(value))
    : Object.values(availableOperators);

  operators.forEach((operatorName: string) => {
    const fc = itc.getFieldConfig(fieldName);
    // unwrap from GraphQLNonNull and GraphQLList, if present
    let fieldType: GraphQLInputType | InputTypeComposer | string = getNamedType(
      fc.type
    ) as GraphQLInputType;

    // just treat enums as strings
    if (getNamedType(fc.type) instanceof GraphQLEnumType) {
      fieldType = 'String';
    }

    if (fieldType) {
      if (['in', 'nin', 'in[]', 'nin[]'].includes(operatorName)) {
        // wrap with GraphQLList, if operator required this with `[]`
        const newName = operatorName.slice(-2) === '[]' ? operatorName.slice(0, -2) : operatorName;
        fields[newName] = {
          ...fc,
          type: [fieldType],
        } as any;
      } else {
        if (operatorName === 'exists') {
          fieldType = 'Boolean';
        } else if (operatorName === 'regex') {
          fieldType = GraphQLRegExpAsString;
        }
        fields[operatorName] = {
          ...fc,
          type: fieldType,
        } as any;
      }
    }
  });
  return fields;
}

export type OperatorsConfig = {
  [k: string]: OperatorsConfig | string[];
};

export function _recurseSchema(
  inputITC: InputTypeComposer,
  sourceITC: InputTypeComposer<any>,
  typeName: string,
  schema: Schema | SchemaType | VirtualType | null,
  pathName: string | null,
  operatorsOpts: FilterOperatorsOpts,
  onlyIndexed: boolean
): void {
  const { schemaComposer } = sourceITC;

  Object.keys(sourceITC.getFields()).forEach((fieldName: string) => {
    const fieldTC = sourceITC.getFieldTC(fieldName);
    const fieldType = sourceITC.getFieldTC(fieldName).getType();

    // alias
    const fullPath = pathName ? `${pathName}.${fieldName}` : fieldName;
    let schemaType;
    const _schema = schema as any;
    if (_schema) {
      if (_schema.pathType) {
        const pathType = _schema.pathType(fullPath);
        schemaType = _schema.path(fullPath);
        if (pathType === 'virtual') schemaType = _schema.virtualpath(fullPath);
        if (pathType === 'nested') schemaType = null;
      } else if (typeof _schema.path === 'string') {
        schemaType = null; // array
      }
    }

    // Need to dig into this space
    const isIndexed: boolean = schemaType?.options?.index || fieldName === '_id';

    const hasOperatorsConfig =
      (operatorsOpts &&
        operatorsOpts[fieldName] &&
        Object.keys(operatorsOpts[fieldName]).length >= 1) ||
      (operatorsOpts && Array.isArray(operatorsOpts[fieldName]));
    const operatorsConfig = hasOperatorsConfig
      ? operatorsOpts && operatorsOpts[fieldName]
      : !(operatorsOpts === false);

    // prevent infinite recursion
    if (sourceITC.getType() === fieldType) return;

    if (fieldType instanceof GraphQLScalarType) {
      const newITC = schemaComposer.createInputTC(`${fieldName}${typeName}`);
      if ((onlyIndexed && isIndexed) || hasOperatorsConfig || !operatorsOpts) {
        newITC.addFields(_availableOperatorsFields(fieldName, sourceITC, operatorsConfig));
        inputITC.addFields({
          [fieldName]: newITC,
        });
      }
    } else if (fieldType instanceof GraphQLInputObjectType) {
      const newITC = schemaComposer.createInputTC(`${fieldName}${typeName}`);

      _recurseSchema(
        newITC,
        fieldTC as InputTypeComposer,
        `${upperFirst(fieldName)}${typeName}`,
        schemaType || schema,
        `${fieldName}`,
        operatorsConfig as FilterOperatorsOpts,
        onlyIndexed
      );
      if ((onlyIndexed && isIndexed) || hasOperatorsConfig || !operatorsOpts) {
        inputITC.addFields({
          [fieldName]: newITC,
        });
      }
    } else if (fieldType instanceof GraphQLEnumType) {
      const newITC = schemaComposer.createInputTC({
        name: `${fieldName}${typeName}`,
        fields: {},
      });
      if ((onlyIndexed && isIndexed) || hasOperatorsConfig || !operatorsOpts) {<commit_msg>refactor: typename must be in CamelCase<commit_after>/* eslint-disable no-use-before-define */

import {
  getNamedType,
  GraphQLInputType,
  GraphQLInputObjectType,
  GraphQLScalarType,
  GraphQLEnumType,
} from 'graphql-compose/lib/graphql';
import type { Model, Schema, SchemaType, VirtualType } from 'mongoose';
import type { InputTypeComposer } from 'graphql-compose';
import type { InputTypeComposerFieldConfigAsObjectDefinition } from 'graphql-compose';

import { upperFirst } from '../../utils';
import type { FilterHelperArgsOpts } from './filter';
import GraphQLRegExpAsString from '../../types/RegExpAsString';

export type FilterOperatorNames =
  | 'gt'
  | 'gte'
  | 'lt'
  | 'lte'
  | 'ne'
  | 'in[]'
  | 'nin[]'
  | 'regex'
  | 'exists';

export enum availableOperators {
  GT = 'gt',
  GTE = 'gte',
  LT = 'lt',
  LTE = 'lte',
  NE = 'ne',
  IN = 'in',
  NIN = 'nin',
  REGEX = 'regex',
  EXISTS = 'exists',
}

export type FilterOperatorsOpts =
  | {
      [fieldName: string]: FilterOperatorsOpts | FilterOperatorNames[] | false;
    }
  | false;

export const OPERATORS_FIELDNAME = '_operators';

export function addFilterOperators(
  itc: InputTypeComposer<any>,
  model: Model<any>,
  opts: FilterHelperArgsOpts
): void {
  if (!{}.hasOwnProperty.call(opts, 'operators') || opts.operators !== false) {
    _createOperatorsField(
      itc,
      `Operators${itc.getTypeName() || ''}`,
      model,
      opts.operators || false,
      opts.onlyIndexed
    );
  }

  itc.addFields({
    OR: [itc.getTypeNonNull()],
    AND: [itc.getTypeNonNull()],
  });
}

export function _availableOperatorsFields(
  fieldName: string,
  itc: InputTypeComposer,
  operatorsConfig: any
): Record<string, InputTypeComposerFieldConfigAsObjectDefinition> {
  const fields: Record<string, InputTypeComposerFieldConfigAsObjectDefinition> = {};

  const operators = Array.isArray(operatorsConfig)
    ? operatorsConfig.filter((value) => Object.values(availableOperators).includes(value))
    : Object.values(availableOperators);

  operators.forEach((operatorName: string) => {
    const fc = itc.getFieldConfig(fieldName);
    // unwrap from GraphQLNonNull and GraphQLList, if present
    let fieldType: GraphQLInputType | InputTypeComposer | string = getNamedType(
      fc.type
    ) as GraphQLInputType;

    // just treat enums as strings
    if (getNamedType(fc.type) instanceof GraphQLEnumType) {
      fieldType = 'String';
    }

    if (fieldType) {
      if (['in', 'nin', 'in[]', 'nin[]'].includes(operatorName)) {
        // wrap with GraphQLList, if operator required this with `[]`
        const newName = operatorName.slice(-2) === '[]' ? operatorName.slice(0, -2) : operatorName;
        fields[newName] = {
          ...fc,
          type: [fieldType],
        } as any;
      } else {
        if (operatorName === 'exists') {
          fieldType = 'Boolean';
        } else if (operatorName === 'regex') {
          fieldType = GraphQLRegExpAsString;
        }
        fields[operatorName] = {
          ...fc,
          type: fieldType,
        } as any;
      }
    }
  });
  return fields;
}

export type OperatorsConfig = {
  [k: string]: OperatorsConfig | string[];
};

export function _recurseSchema(
  inputITC: InputTypeComposer,
  sourceITC: InputTypeComposer<any>,
  typeName: string,
  schema: Schema | SchemaType | VirtualType | null,
  pathName: string | null,
  operatorsOpts: FilterOperatorsOpts,
  onlyIndexed: boolean
): void {
  const { schemaComposer } = sourceITC;

  Object.keys(sourceITC.getFields()).forEach((fieldName: string) => {
    const fieldTC = sourceITC.getFieldTC(fieldName);
    const fieldType = sourceITC.getFieldTC(fieldName).getType();

    // alias
    const fullPath = pathName ? `${pathName}.${fieldName}` : fieldName;
    let schemaType;
    const _schema = schema as any;
    if (_schema) {
      if (_schema.pathType) {
        const pathType = _schema.pathType(fullPath);
        schemaType = _schema.path(fullPath);
        if (pathType === 'virtual') schemaType = _schema.virtualpath(fullPath);
        if (pathType === 'nested') schemaType = null;
      } else if (typeof _schema.path === 'string') {
        schemaType = null; // array
      }
    }

    // Need to dig into this space
    const isIndexed: boolean = schemaType?.options?.index || fieldName === '_id';

    const hasOperatorsConfig =
      (operatorsOpts &&
        operatorsOpts[fieldName] &&
        Object.keys(operatorsOpts[fieldName]).length >= 1) ||
      (operatorsOpts && Array.isArray(operatorsOpts[fieldName]));
    const operatorsConfig = hasOperatorsConfig
      ? operatorsOpts && operatorsOpts[fieldName]
      : !(operatorsOpts === false);

    // prevent infinite recursion
    if (sourceITC.getType() === fieldType) return;

    if (fieldType instanceof GraphQLScalarType) {
      const newITC = schemaComposer.createInputTC(`${upperFirst(fieldName)}${typeName}`);
      if ((onlyIndexed && isIndexed) || hasOperatorsConfig || !operatorsOpts) {
        newITC.addFields(_availableOperatorsFields(fieldName, sourceITC, operatorsConfig));
        inputITC.addFields({
          [fieldName]: newITC,
        });
      }
    } else if (fieldType instanceof GraphQLInputObjectType) {
      const newITC = schemaComposer.createInputTC(`${upperFirst(fieldName)}${typeName}`);

      _recurseSchema(
        newITC,
        fieldTC as InputTypeComposer,
        `${upperFirst(fieldName)}${typeName}`,
        schemaType || schema,
        `${fieldName}`,
        operatorsConfig as FilterOperatorsOpts,
        onlyIndexed
      );
      if ((onlyIndexed && isIndexed) || hasOperatorsConfig || !operatorsOpts) {
        inputITC.addFields({
          [fieldName]: newITC,
        });
      }
    } else if (fieldType instanceof GraphQLEnumType) {
      const newITC = schemaComposer.createInputTC({
        name: `${upperFirst(fieldName)}${typeName}`,
        fields: {},
      });
      if ((onlyIndexed && isIndexed) || hasOperatorsConfig || !operatorsOpts) {
{'fraction_non_alphanumeric': 0.1327248367107687, 'fraction_numerical': 0.000669904538603249, 'mean_word_length': 3.205281690140845, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 21, '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': '1873528', 'n_tokens_mistral': 3666, 'n_tokens_neox': 3700, 'n_words': 789}