All files / domains/arg compiler.ts

89.06% Statements 57/64
90.48% Branches 38/42
90% Functions 9/10
90.16% Lines 55/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 14920x               20x 20x 20x 20x 20x 20x 20x   56x 56x 38x 38x 5x   33x   56x 38x   56x     56x 38x 38x 2x   36x 1x   35x 1x     52x     25x 25x 22x   25x     52x 52x   52x 3x   49x 49x 49x 23x   26x 26x 34x 34x   34x 9x   25x 25x         26x     94x 94x   94x 38x   56x 56x     52x   20x                                                                                                                
import {
  GraphQLFieldConfigArgumentMap,
  GraphQLType,
  GraphQLInputType,
  isInputType,
  GraphQLNonNull,
} from 'graphql';
import { resolveType, getParameterNames } from '~/services/utils';
import { injectorRegistry } from '~/domains/inject';
import { ArgsIndex, argRegistry } from './registry';
import { ArgError } from './error';
import { defaultArgOptions } from './options';
 
function compileInferedAndRegisterdArgs(
  infered: any[],
  registeredArgs: ArgsIndex = {},
) {
  const argsMerged = infered.map((inferedType, index) => {
    const registered = registeredArgs[index];
    if (registered && registered.type) {
      return registered.type;
    }
    return inferedType;
  });
 
  const resolvedArgs = argsMerged.map((rawType, index) => {
    return resolveType(rawType, true);
  });
 
  return resolvedArgs;
}
 
function validateArgs(
  target: Function,
  fieldName: string,
  types: GraphQLType[],
): types is GraphQLInputType[] {
  types.forEach((argType, argIndex) => {
    const isInjectedArg = injectorRegistry.has(target, fieldName, argIndex);
 
    if (!isInjectedArg && !argType) {
      throw new ArgError(
        target,
        fieldName,
        argIndex,
        `Could not infer type of argument. Make sure to use native GraphQLInputType, native scalar like 'String' or class decorated with @InputObjectType`,
      );
    }
 
    if (!isInjectedArg && !isInputType(argType)) {
      throw new ArgError(
        target,
        fieldName,
        argIndex,
        `Argument has incorrect type. Make sure to use native GraphQLInputType, native scalar like 'String' or class decorated with @InputObjectType`,
      );
    }
 
    if (isInjectedArg && argRegistry.has(target, fieldName, argIndex)) {
      throw new ArgError(
        target,
        fieldName,
        argIndex,
        `Argument cannot be marked wiht both @Arg and @Inject or custom injector`,
      );
    }
  });
  return true;
}
 
function enhanceType(originalType: GraphQLInputType, isNullable: boolean) {
  let finalType = originalType;
  if (!isNullable) {
    finalType = new GraphQLNonNull(finalType);
  }
  return finalType;
}
 
function convertArgsArrayToArgsMap(
  target: Function,
  fieldName: string,
  argsTypes: GraphQLInputType[],
  registeredArgs: ArgsIndex = {},
): GraphQLFieldConfigArgumentMap {
  const fieldDescriptor = Object.getOwnPropertyDescriptor(
    target.prototype,
    fieldName,
  );I

  // in case of getters, field arguments are not relevant
  if (fieldDescriptor.get) {
    return {};
  }
 
  const functionDefinition = target.prototype[fieldName];
  const argNames = getParameterNames(functionDefinition);
 
  if (!argNames || !argNames.length) {
    return {};
  }
 
  const argsMap: GraphQLFieldConfigArgumentMap = {};
  argNames.forEach((argName, index) => {
    const argConfig = registeredArgs[index] || { ...defaultArgOptions };
    const argType = argsTypes[index];
 
    // don't publish args marked as auto Injected
    if (injectorRegistry.has(target, fieldName, index)) {
      return;
    }
 
    let finalType = enhanceType(argType, argConfig.isNullable);
 
    argsMap[argName] = {
      type: finalType,
      description: argConfig.description,
    };
  });
  return argsMap;
}
 
export function compileFieldArgs(
  target: Function,
  fieldName: string,
): GraphQLFieldConfigArgumentMap {
  const registeredArgs = argRegistry.getAll(target)[fieldName];
  const inferedRawArgs = Reflect.getMetadata(
    'design:paramtypes',
    target.prototype,
    fieldName,
  );
 
  // There are no arguments
  if (!inferedRawArgs) {
    return {};
  }
 
  const argTypes = compileInferedAndRegisterdArgs(
    inferedRawArgs,
    registeredArgs,
  );
 
  if (!validateArgs(target, fieldName, argTypes)) {
    return;
  }
 
  return convertArgsArrayToArgsMap(target, fieldName, argTypes, registeredArgs);
}