graphql#GraphQLOutputType TypeScript Examples

The following examples show how to use graphql#GraphQLOutputType. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From tql with MIT License 7 votes vote down vote up
export function listType(type: GraphQLOutputType | GraphQLInputType): boolean {
  if (type instanceof GraphQLNonNull) {
    return listType(type.ofType);
  } else if (type instanceof GraphQLList) {
    return true;
  } else {
    return false;
  }
}
Example #2
Source File: utils.ts    From tql with MIT License 7 votes vote down vote up
export function outputType(type: GraphQLOutputType): GraphQLOutputType {
  if (type instanceof GraphQLNonNull) {
    return outputType(type.ofType);
  } else if (type instanceof GraphQLList) {
    return outputType(type.ofType);
  } else {
    return type;
  }
}
Example #3
Source File: schema.ts    From squid with GNU General Public License v3.0 6 votes vote down vote up
function unwrapList(type: GraphQLOutputType): DeepList {
    let nulls: boolean[] = []
    while (type instanceof GraphQLList) {
        type = type.ofType
        if (type instanceof GraphQLNonNull) {
            nulls.push(false)
            type = type.ofType
        } else {
            nulls.push(true)
        }
    }
    return {item: type, nulls}
}
Example #4
Source File: schema-resolver.ts    From graphql-mesh with MIT License 6 votes vote down vote up
getFieldConfig(operation: SoapOperation): GraphQLFieldConfig<any, any> {
    const args: GraphQLFieldConfigArgumentMap = this.createSoapOperationFieldArgs(operation);
    const returnType: GraphQLOutputType = this.resolveSoapOperationReturnType(operation);
    const resolver: GraphQLFieldResolver<any, any, any> = this.createSoapOperationFieldResolver(operation);
    return {
      type: returnType,
      description: `Operation ${operation.name()}, port ${operation.port().name()}, service ${operation
        .service()
        .name()}`,
      args,
      resolve: resolver,
    };
  }
Example #5
Source File: schema-resolver.ts    From graphql-mesh with MIT License 6 votes vote down vote up
resolve(input: { type: SoapType; isList: boolean }): GraphQLOutputType {
    try {
      const type: GraphQLOutputType = this.resolveOutputType(input.type);
      return input.isList ? new GraphQLList(type) : type;
    } catch (err) {
      const errStacked = new Error(`could not resolve output type for ${util.inspect(input)}`);
      errStacked.stack += '\nCaused by: ' + err.stack;
      throw errStacked;
    }
  }
Example #6
Source File: schema-resolver.ts    From graphql-mesh with MIT License 6 votes vote down vote up
private resolveOutputType(soapType: SoapType): GraphQLOutputType {
    if (this.alreadyResolvedOutputTypes.has(soapType)) {
      return this.alreadyResolvedOutputTypes.get(soapType);
    }

    if (typeof soapType === 'string') {
      const customType: GraphQLOutputType = this.options.customResolver.outputType(soapType);
      if (customType) {
        this.alreadyResolvedOutputTypes.set(soapType, customType);
        return customType;
      }
    } else if (soapType?.name && soapType?.fields?.length > 0) {
      const objectType: GraphQLObjectType = this.createObjectType(soapType);
      if (objectType) {
        this.alreadyResolvedOutputTypes.set(soapType, objectType);
        return objectType;
      }
    }

    this.logger.warn(`could not resolve output type '`, soapType, `'; using GraphQLJSON instead`);
    this.alreadyResolvedOutputTypes.set(soapType, GraphQLJSON);
    return GraphQLJSON;
  }
Example #7
Source File: utils.ts    From graphql-eslint with MIT License 6 votes vote down vote up
export function getBaseType(type: GraphQLOutputType): GraphQLNamedType {
  if (isNonNullType(type) || isListType(type)) {
    return getBaseType(type.ofType);
  }
  return type;
}
Example #8
Source File: schema-resolver.ts    From graphql-mesh with MIT License 5 votes vote down vote up
private alreadyResolvedOutputTypes: Map<SoapType, GraphQLOutputType> = new Map();
Example #9
Source File: schema.ts    From squid with GNU General Public License v3.0 5 votes vote down vote up
function asNonNull(f: GraphQLField<any, any>): GraphQLOutputType {
    let type = f.type
    if (type instanceof GraphQLNonNull) {
        type = type.ofType
    }
    return type
}
Example #10
Source File: renderTyping.ts    From genql with MIT License 5 votes vote down vote up
renderTyping = (
  type: GraphQLOutputType | GraphQLInputType,
  undefinableValues: boolean,
  undefinableFields: boolean,
  root = true,
  wrap: any = undefined
) => render(type, false, root, undefinableValues, undefinableFields, wrap)
Example #11
Source File: renderTyping.ts    From genql with MIT License 5 votes vote down vote up
render = (
  type: GraphQLOutputType | GraphQLInputType,
  nonNull: boolean,
  root: boolean,
  undefinableValues: boolean,
  undefinableFields: boolean,
  wrap: (x: string) => string = x => x
): string => {
    
  if (root) {
    if (undefinableFields) {
      if (isNonNullType(type)) {
        return `: ${render(type.ofType, true, false, undefinableValues, undefinableFields, wrap)}`
      } else {
        const rendered = render(type, true, false, undefinableValues, undefinableFields, wrap)
        return undefinableValues ? `?: ${rendered}` : `?: (${rendered} | null)`
      }
    } else {
      return `: ${render(type, false, false, undefinableValues, undefinableFields, wrap)}`
    }
  }

  if (isNamedType(type)) {
    let typeName = type.name

    // if is a scalar use the scalar interface to not expose reserved words
    if (isScalarType(type)) {
      typeName = `Scalars['${typeName}']`
    }

    const typing = wrap(typeName)

    if (undefinableValues) {
      return nonNull ? typing : `(${typing} | undefined)`
    } else {
      return nonNull ? typing : `(${typing} | null)`
    }
  }

  if (isListType(type)) {
    const typing = `${render(type.ofType, false, false, undefinableValues, undefinableFields, wrap)}[]`

    if (undefinableValues) {
      return nonNull ? typing : `(${typing} | undefined)`
    } else {
      return nonNull ? typing : `(${typing} | null)`
    }
  }

  return render((<GraphQLNonNull<any>>type).ofType, true, false, undefinableValues, undefinableFields, wrap)
}
Example #12
Source File: context.ts    From proto2graphql with MIT License 5 votes vote down vote up
export function setType(type: GraphQLNamedType, context: Context) {
  context.types[type.name] = type as GraphQLOutputType;
}
Example #13
Source File: addExecutionLogicToComposer.ts    From graphql-mesh with MIT License 5 votes vote down vote up
isListTypeOrNonNullListType = memoize1(function isListTypeOrNonNullListType(type: GraphQLOutputType) {
  if (isNonNullType(type)) {
    return isListType(type.ofType);
  }
  return isListType(type);
})
Example #14
Source File: schema-resolver.ts    From graphql-mesh with MIT License 5 votes vote down vote up
resolveSoapOperationReturnType(operation: SoapOperation): GraphQLOutputType {
    return this.outputResolver.resolve(operation.output());
  }
Example #15
Source File: custom-type-resolver.ts    From graphql-mesh with MIT License 5 votes vote down vote up
outputType(typeName: string): GraphQLOutputType {
    return this.resolve(typeName);
  }
Example #16
Source File: index.ts    From graphql-mesh with MIT License 4 votes vote down vote up
async getMeshSource() {
    const { schemaHeaders, serviceName, operationHeaders } = this.config;

    const thriftAST = await this.idl.getWithSet(async () => {
      const rawThrift = await readFileOrUrl<string>(this.config.idl, {
        allowUnknownExtensions: true,
        cwd: this.baseDir,
        headers: schemaHeaders,
      });
      const parseResult = parse(rawThrift, { organize: false });
      if (parseResult.type === SyntaxType.ThriftErrors) {
        throw new AggregateError(parseResult.errors);
      }
      return parseResult;
    });

    const enumTypeMap = new Map<string, GraphQLEnumType>();
    const outputTypeMap = new Map<string, GraphQLOutputType>();
    const inputTypeMap = new Map<string, GraphQLInputType>();
    const rootFields: GraphQLFieldConfigMap<any, any> = {};
    const annotations: IThriftAnnotations = {};
    const methodAnnotations: IMethodAnnotations = {};
    const methodNames: string[] = [];
    const methodParameters: {
      [methodName: string]: number;
    } = {};

    type TypeVal = BaseTypeVal | ListTypeVal | SetTypeVal | MapTypeVal | EnumTypeVal | StructTypeVal | VoidTypeVal;
    type BaseTypeVal = {
      id?: number;
      type: TType.BOOL | TType.BYTE | TType.DOUBLE | TType.I16 | TType.I32 | TType.I64 | TType.STRING;
    };
    type ListTypeVal = { id?: number; type: TType.LIST; elementType: TypeVal };
    type SetTypeVal = { id?: number; type: TType.SET; elementType: TypeVal };
    type MapTypeVal = { id?: number; type: TType.MAP; keyType: TypeVal; valType: TypeVal };
    type EnumTypeVal = { id?: number; type: TType.ENUM };
    type StructTypeVal = { id?: number; type: TType.STRUCT; name: string; fields: TypeMap };
    type VoidTypeVal = { id?: number; type: TType.VOID };
    type TypeMap = Record<string, TypeVal>;
    const topTypeMap: TypeMap = {};

    class MeshThriftClient<Context = any> extends ThriftClient<Context> {
      public static readonly serviceName: string = serviceName;
      public static readonly annotations: IThriftAnnotations = annotations;
      public static readonly methodAnnotations: IMethodAnnotations = methodAnnotations;
      public static readonly methodNames: Array<string> = methodNames;
      public readonly _serviceName: string = serviceName;
      public readonly _annotations: IThriftAnnotations = annotations;
      public readonly _methodAnnotations: IMethodAnnotations = methodAnnotations;
      public readonly _methodNames: Array<string> = methodNames;
      public readonly _methodParameters?: {
        [methodName: string]: number;
      } = methodParameters;

      writeType(typeVal: TypeVal, value: any, output: TProtocol) {
        switch (typeVal.type) {
          case TType.BOOL:
            output.writeBool(value);
            break;
          case TType.BYTE:
            output.writeByte(value);
            break;
          case TType.DOUBLE:
            output.writeDouble(value);
            break;
          case TType.I16:
            output.writeI16(value);
            break;
          case TType.I32:
            output.writeI32(value);
            break;
          case TType.I64:
            output.writeI64(value.toString());
            break;
          case TType.STRING:
            output.writeString(value);
            break;
          case TType.STRUCT: {
            output.writeStructBegin(typeVal.name);
            const typeMap = typeVal.fields;
            for (const argName in value) {
              const argType = typeMap[argName];
              const argVal = value[argName];
              if (argType) {
                output.writeFieldBegin(argName, argType.type, argType.id);
                this.writeType(argType, argVal, output);
                output.writeFieldEnd();
              }
            }
            output.writeFieldStop();
            output.writeStructEnd();
            break;
          }
          case TType.ENUM:
            // TODO: A
            break;
          case TType.MAP: {
            const keys = Object.keys(value);
            output.writeMapBegin(typeVal.keyType.type, typeVal.valType.type, keys.length);
            for (const key of keys) {
              this.writeType(typeVal.keyType, key, output);
              const val = value[key];
              this.writeType(typeVal.valType, val, output);
            }
            output.writeMapEnd();
            break;
          }
          case TType.LIST:
            output.writeListBegin(typeVal.elementType.type, value.length);
            for (const element of value) {
              this.writeType(typeVal.elementType, element, output);
            }
            output.writeListEnd();
            break;
          case TType.SET:
            output.writeSetBegin(typeVal.elementType.type, value.length);
            for (const element of value) {
              this.writeType(typeVal.elementType, element, output);
            }
            output.writeSetEnd();
            break;
        }
      }

      readType(type: TType, input: TProtocol): any {
        switch (type) {
          case TType.BOOL:
            return input.readBool();
          case TType.BYTE:
            return input.readByte();
          case TType.DOUBLE:
            return input.readDouble();
          case TType.I16:
            return input.readI16();
          case TType.I32:
            return input.readI32();
          case TType.I64:
            return BigInt(input.readI64().toString());
          case TType.STRING:
            return input.readString();
          case TType.STRUCT: {
            const result: any = {};
            input.readStructBegin();
            while (true) {
              const field: IThriftField = input.readFieldBegin();
              const fieldType = field.fieldType;
              const fieldName = field.fieldName || 'success';
              if (fieldType === TType.STOP) {
                break;
              }
              result[fieldName] = this.readType(fieldType, input);
              input.readFieldEnd();
            }
            input.readStructEnd();
            return result;
          }
          case TType.ENUM:
            // TODO: A
            break;
          case TType.MAP: {
            const result: any = {};
            const map = input.readMapBegin();
            for (let i = 0; i < map.size; i++) {
              const key = this.readType(map.keyType, input);
              const value = this.readType(map.valueType, input);
              result[key] = value;
            }
            input.readMapEnd();
            return result;
          }
          case TType.LIST: {
            const result: any[] = [];
            const list = input.readListBegin();
            for (let i = 0; i < list.size; i++) {
              const element = this.readType(list.elementType, input);
              result.push(element);
            }
            input.readListEnd();
            return result;
          }
          case TType.SET: {
            const result: any[] = [];
            const list = input.readSetBegin();
            for (let i = 0; i < list.size; i++) {
              const element = this.readType(list.elementType, input);
              result.push(element);
            }
            input.readSetEnd();
            return result;
          }
        }
      }

      async doRequest(methodName: string, args: any, fields: TypeMap, context?: any) {
        const Transport = this.transport;
        const Protocol = this.protocol;
        const writer: TTransport = new Transport();
        const output: TProtocol = new Protocol(writer);
        const id = this.incrementRequestId();
        output.writeMessageBegin(methodName, MessageType.CALL, id);
        this.writeType(
          {
            name: pascalCase(methodName) + '__Args',
            type: TType.STRUCT,
            fields,
            id,
          },
          args,
          output
        );
        output.writeMessageEnd();
        const data: Buffer = await this.connection.send(writer.flush(), context);
        const reader: TTransport = this.transport.receiver(data);
        const input: TProtocol = new Protocol(reader);
        const { fieldName, messageType }: IThriftMessage = input.readMessageBegin();
        if (fieldName === methodName) {
          if (messageType === MessageType.EXCEPTION) {
            const err: TApplicationException = TApplicationExceptionCodec.decode(input);
            input.readMessageEnd();
            return Promise.reject(err);
          } else {
            const result = this.readType(TType.STRUCT, input);
            input.readMessageEnd();
            if (result.success != null) {
              return result.success;
            } else {
              throw new TApplicationException(
                TApplicationExceptionType.UNKNOWN,
                methodName + ' failed: unknown result'
              );
            }
          }
        } else {
          throw new TApplicationException(
            TApplicationExceptionType.WRONG_METHOD_NAME,
            'Received a response to an unknown RPC function: ' + fieldName
          );
        }
      }
    }
    const thriftHttpClient = createHttpClient(MeshThriftClient, {
      ...this.config,
      requestOptions: {
        headers: operationHeaders,
      },
    });

    function processComments(comments: Comment[]) {
      return comments.map(comment => comment.value).join('\n');
    }

    function getGraphQLFunctionType(
      functionType: FunctionType,
      id = Math.random()
    ): { outputType: GraphQLOutputType; inputType: GraphQLInputType; typeVal: TypeVal } {
      let inputType: GraphQLInputType;
      let outputType: GraphQLOutputType;
      let typeVal: TypeVal;
      switch (functionType.type) {
        case SyntaxType.BinaryKeyword:
        case SyntaxType.StringKeyword:
          inputType = GraphQLString;
          outputType = GraphQLString;
          break;
        case SyntaxType.DoubleKeyword:
          inputType = GraphQLFloat;
          outputType = GraphQLFloat;
          typeVal = typeVal! || { type: TType.DOUBLE };
          break;
        case SyntaxType.VoidKeyword:
          typeVal = typeVal! || { type: TType.VOID };
          inputType = GraphQLVoid;
          outputType = GraphQLVoid;
          break;
        case SyntaxType.BoolKeyword:
          typeVal = typeVal! || { type: TType.BOOL };
          inputType = GraphQLBoolean;
          outputType = GraphQLBoolean;
          break;
        case SyntaxType.I8Keyword:
          inputType = GraphQLInt;
          outputType = GraphQLInt;
          typeVal = typeVal! || { type: TType.I08 };
          break;
        case SyntaxType.I16Keyword:
          inputType = GraphQLInt;
          outputType = GraphQLInt;
          typeVal = typeVal! || { type: TType.I16 };
          break;
        case SyntaxType.I32Keyword:
          inputType = GraphQLInt;
          outputType = GraphQLInt;
          typeVal = typeVal! || { type: TType.I32 };
          break;
        case SyntaxType.ByteKeyword:
          inputType = GraphQLByte;
          outputType = GraphQLByte;
          typeVal = typeVal! || { type: TType.BYTE };
          break;
        case SyntaxType.I64Keyword:
          inputType = GraphQLBigInt;
          outputType = GraphQLBigInt;
          typeVal = typeVal! || { type: TType.I64 };
          break;
        case SyntaxType.ListType: {
          const ofTypeList = getGraphQLFunctionType(functionType.valueType, id);
          inputType = new GraphQLList(ofTypeList.inputType);
          outputType = new GraphQLList(ofTypeList.outputType);
          typeVal = typeVal! || { type: TType.LIST, elementType: ofTypeList.typeVal };
          break;
        }
        case SyntaxType.SetType: {
          const ofSetType = getGraphQLFunctionType(functionType.valueType, id);
          inputType = new GraphQLList(ofSetType.inputType);
          outputType = new GraphQLList(ofSetType.outputType);
          typeVal = typeVal! || { type: TType.SET, elementType: ofSetType.typeVal };
          break;
        }
        case SyntaxType.MapType: {
          inputType = GraphQLJSON;
          outputType = GraphQLJSON;
          const ofTypeKey = getGraphQLFunctionType(functionType.keyType, id);
          const ofTypeValue = getGraphQLFunctionType(functionType.valueType, id);
          typeVal = typeVal! || { type: TType.MAP, keyType: ofTypeKey.typeVal, valType: ofTypeValue.typeVal };
          break;
        }
        case SyntaxType.Identifier: {
          const typeName = functionType.value;
          if (enumTypeMap.has(typeName)) {
            const enumType = enumTypeMap.get(typeName)!;
            inputType = enumType;
            outputType = enumType;
          }
          if (inputTypeMap.has(typeName)) {
            inputType = inputTypeMap.get(typeName)!;
          }
          if (outputTypeMap.has(typeName)) {
            outputType = outputTypeMap.get(typeName)!;
          }
          typeVal = topTypeMap[typeName];
          break;
        }
        default:
          throw new Error(`Unknown function type: ${util.inspect(functionType)}!`);
      }
      return {
        inputType: inputType!,
        outputType: outputType!,
        typeVal: {
          ...typeVal!,
          id,
        },
      };
    }

    const { args: commonArgs, contextVariables } = parseInterpolationStrings(Object.values(operationHeaders || {}));

    const headersFactory = getInterpolatedHeadersFactory(operationHeaders);

    for (const statement of thriftAST.body) {
      switch (statement.type) {
        case SyntaxType.EnumDefinition:
          enumTypeMap.set(
            statement.name.value,
            new GraphQLEnumType({
              name: statement.name.value,
              description: processComments(statement.comments),
              values: statement.members.reduce(
                (prev, curr) => ({
                  ...prev,
                  [curr.name.value]: {
                    description: processComments(curr.comments),
                    value: curr.name.value,
                  },
                }),
                {} as GraphQLEnumValueConfigMap
              ),
            })
          );
          break;
        case SyntaxType.StructDefinition: {
          const structName = statement.name.value;
          const description = processComments(statement.comments);
          const objectFields: GraphQLFieldConfigMap<any, any> = {};
          const inputObjectFields: GraphQLInputFieldConfigMap = {};
          const structTypeVal: StructTypeVal = {
            id: Math.random(),
            name: structName,
            type: TType.STRUCT,
            fields: {},
          };
          topTypeMap[structName] = structTypeVal;
          const structFieldTypeMap = structTypeVal.fields;
          for (const field of statement.fields) {
            const fieldName = field.name.value;
            let fieldOutputType: GraphQLOutputType;
            let fieldInputType: GraphQLInputType;
            const description = processComments(field.comments);
            const processedFieldTypes = getGraphQLFunctionType(field.fieldType, field.fieldID?.value);
            fieldOutputType = processedFieldTypes.outputType;
            fieldInputType = processedFieldTypes.inputType;

            if (field.requiredness === 'required') {
              fieldOutputType = new GraphQLNonNull(fieldOutputType);
              fieldInputType = new GraphQLNonNull(fieldInputType);
            }

            objectFields[fieldName] = {
              type: fieldOutputType,
              description,
            };
            inputObjectFields[fieldName] = {
              type: fieldInputType,
              description,
            };
            structFieldTypeMap[fieldName] = processedFieldTypes.typeVal;
          }
          outputTypeMap.set(
            structName,
            new GraphQLObjectType({
              name: structName,
              description,
              fields: objectFields,
            })
          );
          inputTypeMap.set(
            structName,
            new GraphQLInputObjectType({
              name: structName + 'Input',
              description,
              fields: inputObjectFields,
            })
          );
          break;
        }
        case SyntaxType.ServiceDefinition:
          for (const fnIndex in statement.functions) {
            const fn = statement.functions[fnIndex];
            const fnName = fn.name.value;
            const description = processComments(fn.comments);
            const { outputType: returnType } = getGraphQLFunctionType(fn.returnType, Number(fnIndex) + 1);
            const args: GraphQLFieldConfigArgumentMap = {};
            for (const argName in commonArgs) {
              const typeNameOrType = commonArgs[argName].type;
              args[argName] = {
                type:
                  typeof typeNameOrType === 'string' ? inputTypeMap.get(typeNameOrType) : typeNameOrType || GraphQLID,
              };
            }
            const fieldTypeMap: TypeMap = {};
            for (const field of fn.fields) {
              const fieldName = field.name.value;
              const fieldDescription = processComments(field.comments);
              let { inputType: fieldType, typeVal } = getGraphQLFunctionType(field.fieldType, field.fieldID?.value);
              if (field.requiredness === 'required') {
                fieldType = new GraphQLNonNull(fieldType);
              }
              args[fieldName] = {
                type: fieldType,
                description: fieldDescription,
              };
              fieldTypeMap[fieldName] = typeVal;
            }
            rootFields[fnName] = {
              type: returnType,
              description,
              args,
              resolve: async (root, args, context, info) =>
                thriftHttpClient.doRequest(fnName, args, fieldTypeMap, {
                  headers: headersFactory({ root, args, context, info, env: process.env }),
                }),
            };
            methodNames.push(fnName);
            methodAnnotations[fnName] = { annotations: {}, fieldAnnotations: {} };
            methodParameters[fnName] = fn.fields.length + 1;
          }
          break;
        case SyntaxType.TypedefDefinition: {
          const { inputType, outputType } = getGraphQLFunctionType(statement.definitionType, Math.random());
          const typeName = statement.name.value;
          inputTypeMap.set(typeName, inputType);
          outputTypeMap.set(typeName, outputType);
          break;
        }
      }
    }

    const queryObjectType = new GraphQLObjectType({
      name: 'Query',
      fields: rootFields,
    });

    const schema = new GraphQLSchema({
      query: queryObjectType,
    });

    return {
      schema,
      contextVariables,
    };
  }
Example #17
Source File: schema_builder.ts    From graphql-mesh with MIT License 4 votes vote down vote up
/**
 * Creates the fields object to be used by an (input) object type
 */
function createFields<TSource, TContext, TArgs>({
  def,
  links,
  operation,
  data,
  iteration,
  isInputObjectType,
  includeHttpDetails,
  logger,
}: CreateFieldsParams<TSource, TContext, TArgs>): GraphQLFieldConfigMap<any, any> | GraphQLInputFieldConfigMap {
  const translationLogger = logger.child('translation');

  let fields: GraphQLFieldConfigMap<any, any> = {};

  if (includeHttpDetails && !isInputObjectType) {
    fields.httpDetails = {
      type: GraphQLJSON,
      resolve: root => root?._openAPIToGraphQL?.data,
    };
  }

  const fieldTypeDefinitions = def.subDefinitions as {
    [fieldName: string]: DataDefinition;
  };

  // Create fields for properties
  for (const fieldTypeKey in fieldTypeDefinitions) {
    const fieldTypeDefinition = fieldTypeDefinitions[fieldTypeKey];
    const fieldSchema = fieldTypeDefinition.schema;

    // Get object type describing the property
    const objectType = getGraphQLType({
      def: fieldTypeDefinition,
      operation,
      data,
      iteration: iteration + 1,
      isInputObjectType,
      includeHttpDetails,
      logger,
    });

    const requiredProperty =
      typeof def.required === 'object' && def.required.includes(fieldTypeKey) && !fieldTypeDefinition.schema.nullable;

    // Finally, add the object type to the fields (using sanitized field name)
    if (objectType) {
      const saneFieldTypeKey = Oas3Tools.sanitize(
        fieldTypeKey,
        !data.options.simpleNames ? Oas3Tools.CaseStyle.camelCase : Oas3Tools.CaseStyle.simple
      );

      const sanePropName = Oas3Tools.storeSaneName(saneFieldTypeKey, fieldTypeKey, data.saneMap, logger);

      fields[sanePropName] = {
        type: (requiredProperty ? new GraphQLNonNull(objectType) : objectType) as GraphQLOutputType,

        description: typeof fieldSchema === 'object' ? fieldSchema.description : null,
      };
    } else {
      handleWarning({
        mitigationType: MitigationTypes.CANNOT_GET_FIELD_TYPE,
        message:
          `Cannot obtain GraphQL type for field '${fieldTypeKey}' in ` +
          `GraphQL type '${JSON.stringify(def.schema)}'.`,
        data,
        logger: translationLogger,
      });
    }
  }

  if (
    typeof links === 'object' && // Links are present
    !isInputObjectType // Only object type (input object types cannot make use of links)
  ) {
    for (const saneLinkKey in links) {
      translationLogger.debug(`Create link '${saneLinkKey}'...`);

      // Check if key is already in fields
      if (saneLinkKey in fields) {
        handleWarning({
          mitigationType: MitigationTypes.LINK_NAME_COLLISION,
          message:
            `Cannot create link '${saneLinkKey}' because parent ` +
            `object type already contains a field with the same (sanitized) name.`,
          data,
          logger: translationLogger,
        });
      } else {
        const link = links[saneLinkKey];

        // Get linked operation
        let linkedOpId;
        // TODO: href is yet another alternative to operationRef and operationId
        if (typeof link.operationId === 'string') {
          linkedOpId = link.operationId;
        } else if (typeof link.operationRef === 'string') {
          linkedOpId = linkOpRefToOpId({
            links,
            linkKey: saneLinkKey,
            operation,
            data,
            logger,
          });
        }

        /**
         * linkedOpId may not be initialized because operationRef may lead to an
         * operation object that does not have an operationId
         */
        if (typeof linkedOpId === 'string' && linkedOpId in data.operations) {
          const linkedOp = data.operations[linkedOpId];

          // Determine parameters provided via link
          const argsFromLink = link.parameters;

          // Get arguments that are not provided by the linked operation
          let dynamicParams = linkedOp.parameters;
          if (typeof argsFromLink === 'object') {
            dynamicParams = dynamicParams.filter(param => {
              return typeof argsFromLink[param.name] === 'undefined';
            });
          }

          // Get resolve function for link
          const linkResolver = data.options.resolverMiddleware(
            () => ({
              operation: linkedOp,
              argsFromLink: argsFromLink as { [key: string]: string },
              data,
              baseUrl: data.options.baseUrl,
              requestOptions: data.options.requestOptions,
              logger,
            }),
            getResolver
          );

          // Get arguments for link
          const args = getArgs({
            parameters: dynamicParams,
            operation: linkedOp,
            data,
            includeHttpDetails,
            logger,
          });

          // Get response object type
          const resObjectType =
            linkedOp.responseDefinition.graphQLType !== undefined
              ? linkedOp.responseDefinition.graphQLType
              : getGraphQLType({
                  def: linkedOp.responseDefinition,
                  operation,
                  data,
                  iteration: iteration + 1,
                  isInputObjectType: false,
                  includeHttpDetails,
                  logger,
                });

          let description = link.description;

          if (data.options.equivalentToMessages && description) {
            description += `\n\nEquivalent to ${linkedOp.operationString}`;
          }

          // Finally, add the object type to the fields (using sanitized field name)
          // TODO: check if fields already has this field name
          fields[saneLinkKey] = {
            type: resObjectType as GraphQLOutputType,
            resolve: linkResolver,
            args,
            description,
          };
        } else {
          handleWarning({
            mitigationType: MitigationTypes.UNRESOLVABLE_LINK,
            message: `Cannot resolve target of link '${saneLinkKey}'`,
            data,
            logger: translationLogger,
          });
        }
      }
    }
  }

  fields = sortObject(fields);
  return fields;
}
Example #18
Source File: index.ts    From graphql-mesh with MIT License 4 votes vote down vote up
/**
 * Creates the field object for the given operation.
 */
function getFieldForOperation<TSource, TContext, TArgs>(
  operation: Operation,
  baseUrl: string,
  data: PreprocessingData<TSource, TContext, TArgs>,
  requestOptions: RequestOptions<TSource, TContext, TArgs>,
  connectOptions: ConnectOptions,
  includeHttpDetails: boolean,
  pubsub: MeshPubSub,
  logger: Logger
): GraphQLFieldConfig<TSource, TContext | SubscriptionContext, TArgs> {
  // Create GraphQL Type for response:
  const type = getGraphQLType({
    def: operation.responseDefinition,
    data,
    operation,
    includeHttpDetails,
    logger,
  }) as GraphQLOutputType;

  const payloadSchemaName = operation.payloadDefinition ? operation.payloadDefinition.graphQLInputObjectTypeName : null;

  const args: Args = getArgs({
    /**
     * Even though these arguments seems redundent because of the operation
     * argument, the function cannot be refactored because it is also used to
     * create arguments for links. The operation argument is really used to pass
     * data to other functions.
     */
    requestPayloadDef: operation.payloadDefinition,
    parameters: operation.parameters,
    operation,
    data,
    includeHttpDetails,
    logger,
  });

  // Get resolver and subscribe function for Subscription fields
  if (operation.operationType === GraphQLOperationType.Subscription) {
    const responseSchemaName = operation.responseDefinition ? operation.responseDefinition.graphQLTypeName : null;

    const resolve = getPublishResolver({
      operation,
      responseName: responseSchemaName,
      data,
      logger,
    });

    const subscribe = getSubscribe({
      operation,
      payloadName: payloadSchemaName,
      data,
      baseUrl,
      connectOptions,
      pubsub,
      logger,
    });

    return {
      type,
      resolve,
      subscribe,
      args,
      description: operation.description,
    };

    // Get resolver for Query and Mutation fields
  } else {
    const resolve = data.options.resolverMiddleware(
      () => ({
        operation,
        payloadName: payloadSchemaName,
        data,
        baseUrl,
        requestOptions,
        logger,
      }),
      getResolver
    );

    return {
      type,
      resolve,
      args,
      description: operation.description,
    };
  }
}
Example #19
Source File: require-id-when-available.ts    From graphql-eslint with MIT License 4 votes vote down vote up
rule: GraphQLESLintRule<[RequireIdWhenAvailableRuleConfig], true> = {
  meta: {
    type: 'suggestion',
    // eslint-disable-next-line eslint-plugin/require-meta-has-suggestions
    hasSuggestions: true,
    docs: {
      category: 'Operations',
      description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
      url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
      requiresSchema: true,
      requiresSiblings: true,
      examples: [
        {
          title: 'Incorrect',
          code: /* GraphQL */ `
            # In your schema
            type User {
              id: ID!
              name: String!
            }

            # Query
            query {
              user {
                name
              }
            }
          `,
        },
        {
          title: 'Correct',
          code: /* GraphQL */ `
            # In your schema
            type User {
              id: ID!
              name: String!
            }

            # Query
            query {
              user {
                id
                name
              }
            }

            # Selecting \`id\` with an alias is also valid
            query {
              user {
                id: name
              }
            }
          `,
        },
      ],
      recommended: true,
    },
    messages: {
      [RULE_ID]:
        "Field{{ pluralSuffix }} {{ fieldName }} must be selected when it's available on a type.\nInclude it in your selection set{{ addition }}.",
    },
    schema: {
      definitions: {
        asString: {
          type: 'string',
        },
        asArray: ARRAY_DEFAULT_OPTIONS,
      },
      type: 'array',
      maxItems: 1,
      items: {
        type: 'object',
        additionalProperties: false,
        properties: {
          fieldName: {
            oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asArray' }],
            default: DEFAULT_ID_FIELD_NAME,
          },
        },
      },
    },
  },
  create(context) {
    const schema = requireGraphQLSchemaFromContext(RULE_ID, context);
    const siblings = requireSiblingsOperations(RULE_ID, context);
    const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
    const idNames = asArray(fieldName);

    // Check selections only in OperationDefinition,
    // skip selections of OperationDefinition and InlineFragment
    const selector = 'OperationDefinition SelectionSet[parent.kind!=/(^OperationDefinition|InlineFragment)$/]';
    const typeInfo = new TypeInfo(schema);

    function checkFragments(node: GraphQLESTreeNode<SelectionSetNode>): void {
      for (const selection of node.selections) {
        if (selection.kind !== Kind.FRAGMENT_SPREAD) {
          continue;
        }

        const [foundSpread] = siblings.getFragment(selection.name.value);
        if (!foundSpread) {
          continue;
        }

        const checkedFragmentSpreads = new Set<string>();
        const visitor = visitWithTypeInfo(typeInfo, {
          SelectionSet(node, key, parent: ASTNode) {
            if (parent.kind === Kind.FRAGMENT_DEFINITION) {
              checkedFragmentSpreads.add(parent.name.value);
            } else if (parent.kind !== Kind.INLINE_FRAGMENT) {
              checkSelections(node, typeInfo.getType(), selection.loc.start, parent, checkedFragmentSpreads);
            }
          },
        });

        visit(foundSpread.document, visitor);
      }
    }

    function checkSelections(
      node: OmitRecursively<SelectionSetNode, 'loc'>,
      type: GraphQLOutputType,
      // Fragment can be placed in separate file
      // Provide actual fragment spread location instead of location in fragment
      loc: ESTree.Position,
      // Can't access to node.parent in GraphQL AST.Node, so pass as argument
      parent: any,
      checkedFragmentSpreads = new Set<string>()
    ): void {
      const rawType = getBaseType(type);
      const isObjectType = rawType instanceof GraphQLObjectType;
      const isInterfaceType = rawType instanceof GraphQLInterfaceType;

      if (!isObjectType && !isInterfaceType) {
        return;
      }
      const fields = rawType.getFields();
      const hasIdFieldInType = idNames.some(name => fields[name]);

      if (!hasIdFieldInType) {
        return;
      }

      function hasIdField({ selections }: typeof node): boolean {
        return selections.some(selection => {
          if (selection.kind === Kind.FIELD) {
            if (selection.alias && idNames.includes(selection.alias.value)) {
              return true;
            }

            return idNames.includes(selection.name.value);
          }

          if (selection.kind === Kind.INLINE_FRAGMENT) {
            return hasIdField(selection.selectionSet);
          }

          if (selection.kind === Kind.FRAGMENT_SPREAD) {
            const [foundSpread] = siblings.getFragment(selection.name.value);
            if (foundSpread) {
              const fragmentSpread = foundSpread.document;
              checkedFragmentSpreads.add(fragmentSpread.name.value);
              return hasIdField(fragmentSpread.selectionSet);
            }
          }
          return false;
        });
      }

      const hasId = hasIdField(node);

      checkFragments(node as GraphQLESTreeNode<SelectionSetNode>);

      if (hasId) {
        return;
      }

      const pluralSuffix = idNames.length > 1 ? 's' : '';
      const fieldName = englishJoinWords(idNames.map(name => `\`${(parent.alias || parent.name).value}.${name}\``));

      const addition =
        checkedFragmentSpreads.size === 0
          ? ''
          : ` or add to used fragment${checkedFragmentSpreads.size > 1 ? 's' : ''} ${englishJoinWords(
              [...checkedFragmentSpreads].map(name => `\`${name}\``)
            )}`;

      const problem: ReportDescriptor = {
        loc,
        messageId: RULE_ID,
        data: {
          pluralSuffix,
          fieldName,
          addition,
        },
      };

      // Don't provide suggestions for selections in fragments as fragment can be in a separate file
      if ('type' in node) {
        problem.suggest = idNames.map(idName => ({
          desc: `Add \`${idName}\` selection`,
          fix: fixer => fixer.insertTextBefore((node as any).selections[0], `${idName} `),
        }));
      }
      context.report(problem);
    }

    return {
      [selector](node: GraphQLESTreeNode<SelectionSetNode, true>) {
        const typeInfo = node.typeInfo();
        if (typeInfo.gqlType) {
          checkSelections(node, typeInfo.gqlType, node.loc.start, node.parent);
        }
      },
    };
  },
}