<commit_before>import {
  parse,
  GraphQLSchema,
  DocumentNode,
  GraphQLField,
  GraphQLCompositeType,
  isCompositeType,
  getNamedType,
  GraphQLType,
  VariableDefinitionNode,
  TypeNode,
  OperationTypeNode,
  GraphQLObjectType,
  GraphQLEnumType,
  DirectiveNode,
  GraphQLInputObjectType,
  GraphQLInputField,
  GraphQLUnionType,
  GraphQLNamedType,
  FieldNode,
} from 'graphql';
import {
  schemaFromInputs,
  isList,
  isNonNullable,
  isEnum,
} from '@gql2ts/util';
import {
  GetChildSelectionsType,
  IChildSelection,
  IComplexTypeSignature,
  IFromQueryOptions,
  FromQuerySignature,
  HandleNamedTypes,
  HandleInputTypes,
  ConvertToTypeSignature,
  ITypeMap,
  IFromQueryReturnValue,
} from '@gql2ts/types';
import {
  DEFAULT_TYPE_MAP,
  DEFAULT_OPTIONS,
} from '@gql2ts/language-typescript';
import {
  GenerateSubtypeCache,
  SubtypeNamerAndDedupe,
  ISubtypeMetadata,
} from './subtype';

const doIt: FromQuerySignature = (schema, query, typeMap = {}, providedOptions = {}) => {
  const TypeMap: ITypeMap = {
    ...DEFAULT_TYPE_MAP,
    ...typeMap
  };

  const {
    wrapList,
    wrapPartial,
    generateSubTypeInterfaceName,
    printType,
    formatInput,
    generateFragmentName,
    generateQueryName,
    formatEnum,
    interfaceBuilder,
    typeBuilder,
    typeJoiner,
    generateInterfaceDeclaration,
    exportFunction,
    postProcessor,
    generateInputName,
    addExtensionsToInterfaceName,
  }: IFromQueryOptions = { ...DEFAULT_OPTIONS, ...providedOptions };

  const getSubtype: SubtypeNamerAndDedupe = GenerateSubtypeCache();

  const parsedSchema: GraphQLSchema = schemaFromInputs(schema);
  const parsedSelection: DocumentNode = parse(query);

  const handleInputObject: (type: GraphQLInputObjectType, isNonNull: boolean) => string = (type, isNonNull) => {
    const variables: GraphQLInputField[] = Object.keys(type.getFields()).map(k => type.getFields()[k]);
    const variableDeclarations: string[] = variables.map(v => formatInput(v.name, true, convertToType(v.type)));
    const builder: string = generateInterfaceDeclaration(variableDeclarations.map(v => v));
    return printType(builder, isNonNull);
  };

  const handleEnum: (type: GraphQLEnumType, isNonNull: boolean) => string = (type, isNonNull) => {
    const decl: string = formatEnum(type.getValues());
    return printType(decl, isNonNull);
  };

  const handleNamedTypeInput: (type: TypeNode, isNonNull: boolean) => string | undefined = (type, isNonNull) => {
    if (type.kind === 'NamedType' && type.name.kind === 'Name' && type.name.value) {
      const newType: GraphQLType = parsedSchema.getType(type.name.value);
      if (newType instanceof GraphQLEnumType) {
        return handleEnum(newType, isNonNull);
      } else if (newType instanceof GraphQLInputObjectType) {
        return handleInputObject(newType, isNonNull);
      }
    }
  };

  const handleRegularType: HandleNamedTypes = (type, isNonNull, replacement) => {
    const typeValue: string = (typeof type.name === 'string') ? type.toString() : type.name.value;
    const showValue: string = replacement || typeValue;
    const show: string = TypeMap[showValue] || (replacement ? showValue : TypeMap.__DEFAULT);
    return printType(show, isNonNull);
  };

  const convertVariable: HandleInputTypes = (type, isNonNull = false, replacement = null) => {
    if (type.kind === 'ListType') {
      return printType(wrapList(convertVariable(type.type, false, replacement)), isNonNull!);
    } else if (type.kind === 'NonNullType') {
      return convertVariable(type.type, true, replacement);
    } else {
      return handleNamedTypeInput(type, isNonNull!) || handleRegularType(type, isNonNull!, replacement!);
    }
  };

  const convertToType: ConvertToTypeSignature = (type, isNonNull = false, replacement = null): string => {
    if (isList(type)) {
      return printType(wrapList(convertToType(type.ofType, false, replacement)), isNonNull!);
    } else if (isNonNullable(type)) {
      return convertToType(type.ofType, true, replacement);
    } else if (isEnum(type)) {
      return handleEnum(type, isNonNull!);
    } else {
      return handleRegularType(type, isNonNull!, replacement!);
    }
  };

  const UndefinedDirectives: Set<string> = new Set(['include', 'skip']);

  const isUndefinedFromDirective: (directives: DirectiveNode[] | undefined) => boolean = directives => {
    if (!directives || !directives.length) { return false; }

    const badDirectives: DirectiveNode[] = directives.filter(d => !UndefinedDirectives.has(d.name.value));
    const hasDirectives: boolean = directives.some(d => UndefinedDirectives.has(d.name.value));

    if (badDirectives.length) {
      console.error('Found some unknown directives:');
      badDirectives.forEach(d => console.error(d.name.value));
    }

    return hasDirectives;
  };

  const getOperationFields: (operation: OperationTypeNode) => GraphQLObjectType | null | undefined = operation => {
    switch (operation) {
      case 'query':
        return parsedSchema.getQueryType();
      case 'mutation':
        return parsedSchema.getMutationType();
      case 'subscription':
        return parsedSchema.getSubscriptionType();
      default:
        throw new Error('Unsupported Operation');
    }
  };

  const wrapPossiblePartial: (possiblePartial: IChildSelection) => string = possiblePartial => {
    if (possiblePartial.isPartial) {
      return wrapPartial(possiblePartial.iface);
    } else {
      return possiblePartial.iface;
    }
  };

  const flattenComplexTypes: (children: IChildSelection[]) => IComplexTypeSignature[] = children => (
    children.reduce((acc, child) => { acc.push(...child.complexTypes); return acc; }, [] as IComplexTypeSignature[])
  );

  type GetField = (operation: OperationTypeNode, selection: FieldNode, parent?: GraphQLType) => GraphQLField<any, any>;
  const getField: GetField = (operation, selection, parent) => {
    if (parent && isCompositeType(parent)) {
      if (parent instanceof GraphQLUnionType) {
        return parent.getTypes().map(t => t.getFields()[selection.name.value]).find(z => !!z)!;
      } else {
        return parent.getFields()[selection.name.value];
      }
    } else {
      const operationFields: GraphQLObjectType | null | undefined = getOperationFields(operation);
      if (operationFields) {
        return operationFields.getFields()[selection.name.value];
      } else {
        throw new Error(`Operation '${operation}' does not exist on this schema`);
      }
    }<commit_msg>restore coverage<commit_after>import {
  parse,
  GraphQLSchema,
  DocumentNode,
  GraphQLField,
  GraphQLCompositeType,
  isCompositeType,
  getNamedType,
  GraphQLType,
  VariableDefinitionNode,
  TypeNode,
  OperationTypeNode,
  GraphQLObjectType,
  GraphQLEnumType,
  DirectiveNode,
  GraphQLInputObjectType,
  GraphQLInputField,
  GraphQLUnionType,
  GraphQLNamedType,
  FieldNode,
} from 'graphql';
import {
  schemaFromInputs,
  isList,
  isNonNullable,
  isEnum,
} from '@gql2ts/util';
import {
  GetChildSelectionsType,
  IChildSelection,
  IComplexTypeSignature,
  IFromQueryOptions,
  FromQuerySignature,
  HandleNamedTypes,
  HandleInputTypes,
  ConvertToTypeSignature,
  ITypeMap,
  IFromQueryReturnValue,
} from '@gql2ts/types';
import {
  DEFAULT_TYPE_MAP,
  DEFAULT_OPTIONS,
} from '@gql2ts/language-typescript';
import {
  GenerateSubtypeCache,
  SubtypeNamerAndDedupe,
  ISubtypeMetadata,
} from './subtype';

const doIt: FromQuerySignature = (schema, query, typeMap = {}, providedOptions = {}) => {
  const TypeMap: ITypeMap = {
    ...DEFAULT_TYPE_MAP,
    ...typeMap
  };

  const {
    wrapList,
    wrapPartial,
    generateSubTypeInterfaceName,
    printType,
    formatInput,
    generateFragmentName,
    generateQueryName,
    formatEnum,
    interfaceBuilder,
    typeBuilder,
    typeJoiner,
    generateInterfaceDeclaration,
    exportFunction,
    postProcessor,
    generateInputName,
    addExtensionsToInterfaceName,
  }: IFromQueryOptions = { ...DEFAULT_OPTIONS, ...providedOptions };

  const getSubtype: SubtypeNamerAndDedupe = GenerateSubtypeCache();

  const parsedSchema: GraphQLSchema = schemaFromInputs(schema);
  const parsedSelection: DocumentNode = parse(query);

  const handleInputObject: (type: GraphQLInputObjectType, isNonNull: boolean) => string = (type, isNonNull) => {
    const variables: GraphQLInputField[] = Object.keys(type.getFields()).map(k => type.getFields()[k]);
    const variableDeclarations: string[] = variables.map(v => formatInput(v.name, true, convertToType(v.type)));
    const builder: string = generateInterfaceDeclaration(variableDeclarations.map(v => v));
    return printType(builder, isNonNull);
  };

  const handleEnum: (type: GraphQLEnumType, isNonNull: boolean) => string = (type, isNonNull) => {
    const decl: string = formatEnum(type.getValues());
    return printType(decl, isNonNull);
  };

  const handleNamedTypeInput: (type: TypeNode, isNonNull: boolean) => string | undefined = (type, isNonNull) => {
    if (type.kind === 'NamedType' && type.name.kind === 'Name' && type.name.value) {
      const newType: GraphQLType = parsedSchema.getType(type.name.value);
      if (newType instanceof GraphQLEnumType) {
        return handleEnum(newType, isNonNull);
      } else if (newType instanceof GraphQLInputObjectType) {
        return handleInputObject(newType, isNonNull);
      }
    }
  };

  const handleRegularType: HandleNamedTypes = (type, isNonNull, replacement) => {
    const typeValue: string = (typeof type.name === 'string') ? type.toString() : type.name.value;
    const showValue: string = replacement || typeValue;
    const show: string = TypeMap[showValue] || (replacement ? showValue : TypeMap.__DEFAULT);
    return printType(show, isNonNull);
  };

  const convertVariable: HandleInputTypes = (type, isNonNull = false, replacement = null) => {
    if (type.kind === 'ListType') {
      return printType(wrapList(convertVariable(type.type, false, replacement)), isNonNull!);
    } else if (type.kind === 'NonNullType') {
      return convertVariable(type.type, true, replacement);
    } else {
      return handleNamedTypeInput(type, isNonNull!) || handleRegularType(type, isNonNull!, replacement!);
    }
  };

  const convertToType: ConvertToTypeSignature = (type, isNonNull = false, replacement = null): string => {
    if (isList(type)) {
      return printType(wrapList(convertToType(type.ofType, false, replacement)), isNonNull!);
    } else if (isNonNullable(type)) {
      return convertToType(type.ofType, true, replacement);
    } else if (isEnum(type)) {
      return handleEnum(type, isNonNull!);
    } else {
      return handleRegularType(type, isNonNull!, replacement!);
    }
  };

  const UndefinedDirectives: Set<string> = new Set(['include', 'skip']);

  const isUndefinedFromDirective: (directives: DirectiveNode[] | undefined) => boolean = directives => {
    if (!directives || !directives.length) { return false; }

    const badDirectives: DirectiveNode[] = directives.filter(d => !UndefinedDirectives.has(d.name.value));
    const hasDirectives: boolean = directives.some(d => UndefinedDirectives.has(d.name.value));

    if (badDirectives.length) {
      console.error('Found some unknown directives:');
      badDirectives.forEach(d => console.error(d.name.value));
    }

    return hasDirectives;
  };

  const getOperationFields: (operation: OperationTypeNode) => GraphQLObjectType | null | undefined = operation => {
    switch (operation) {
      case 'query':
        return parsedSchema.getQueryType();
      case 'mutation':
        return parsedSchema.getMutationType();
      case 'subscription':
        return parsedSchema.getSubscriptionType();
      default:
        throw new Error('Unsupported Operation');
    }
  };

  const wrapPossiblePartial: (possiblePartial: IChildSelection) => string = possiblePartial => {
    if (possiblePartial.isPartial) {
      return wrapPartial(possiblePartial.iface);
    } else {
      return possiblePartial.iface;
    }
  };

  const flattenComplexTypes: (children: IChildSelection[]) => IComplexTypeSignature[] = children => (
    children.reduce((acc, child) => { acc.push(...child.complexTypes); return acc; }, [] as IComplexTypeSignature[])
  );

  type GetField = (operation: OperationTypeNode, selection: FieldNode, parent?: GraphQLType) => GraphQLField<any, any>;
  const getField: GetField = (operation, selection, parent) => {
    if (parent && isCompositeType(parent)) {
      if (parent instanceof GraphQLUnionType) {
        return parent.getTypes().map(t => t.getFields()[selection.name.value]).find(z => !!z)!;
      } else {
        return parent.getFields()[selection.name.value];
      }
    } else {
      const operationFields: GraphQLObjectType | null | undefined = getOperationFields(operation);
      // operation is taken from the schema, so it should never be falsy
      return operationFields!.getFields()[selection.name.value];
    }
{'fraction_non_alphanumeric': 0.12210558814448905, 'fraction_numerical': 0.0004631058968817536, 'mean_word_length': 4.117298578199052, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 65, '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': '342108', 'n_tokens_mistral': 3790, 'n_tokens_neox': 3784, 'n_words': 828}