graphql#GraphQLInputObjectType TypeScript Examples

The following examples show how to use graphql#GraphQLInputObjectType. 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: inputObjectType.ts    From genql with MIT License 6 votes vote down vote up
inputObjectType = (type: GraphQLInputObjectType, ctx: RenderContext) => {
  let fields = type.getFields()

    if (ctx.config?.sortProperties) {
        fields = sortKeys(fields)
    }

  
  const fieldStrings = Object.keys(fields).map(fieldName => {
    const field = fields[fieldName]
    return `${argumentComment(field)}${field.name}${renderTyping(field.type, false, true)}`
  })

  ctx.addCodeBlock(`${typeComment(type)}export interface ${type.name} {${fieldStrings.join(',')}}`)
}
Example #2
Source File: printers.ts    From tql with MIT License 6 votes vote down vote up
printInputType = (type: GraphQLInputType): string => {
  const isList = listType(type);
  const base = inputType(type);

  return (
    (() => {
      if (base instanceof GraphQLScalarType) {
        return toPrimitive(base);
      } else if (base instanceof GraphQLEnumType) {
        return base.name;
      } else if (base instanceof GraphQLInputObjectType) {
        return "I" + base.name;
      } else {
        throw new Error("Unable to render inputType.");
      }
    })() + (isList ? "[]" : "")
  );
}
Example #3
Source File: withOperators.ts    From payload with MIT License 6 votes vote down vote up
withOperators = (field: FieldAffectingData, type: GraphQLType, parentName: string, operators: string[]): GraphQLInputObjectType => {
  const name = `${combineParentName(parentName, field.name)}_operator`;
  const listOperators = ['in', 'not_in', 'all'];

  if (!field.required) operators.push('exists');

  return new GraphQLInputObjectType({
    name,
    fields: operators.reduce((fields, operator) => {
      let gqlType: GraphQLType;
      if (listOperators.indexOf(operator) > -1) {
        gqlType = new GraphQLList(type);
      } else if (operator === 'exists') {
        gqlType = GraphQLBoolean;
      } else {
        gqlType = type;
      }
      return {
        ...fields,
        [operator]: {
          type: gqlType,
        },
      };
    }, {}),
  });
}
Example #4
Source File: schema-resolver.ts    From graphql-mesh with MIT License 6 votes vote down vote up
private resolveInputType(soapType: SoapType): GraphQLInputType {
    if (this.alreadyResolved.has(soapType)) {
      return this.alreadyResolved.get(soapType);
    }

    if (typeof soapType === 'string') {
      const customType: GraphQLInputType = this.options.customResolver.inputType(soapType);
      if (customType) {
        this.alreadyResolved.set(soapType, customType);
        return customType;
      }
    } else if (soapType?.name && soapType?.fields?.length > 0) {
      const objectType: GraphQLInputObjectType = this.createObjectType(soapType);
      if (objectType) {
        this.alreadyResolved.set(soapType, objectType);
        return objectType;
      }
    }

    this.logger.warn(`could not resolve input type '${soapType}'; using GraphQLString`);
    this.alreadyResolved.set(soapType, GraphQLString);
    return GraphQLString;
  }
Example #5
Source File: Generator.ts    From graphql-ts-client with MIT License 6 votes vote down vote up
private async generateInputTypes(inputTypes: GraphQLInputObjectType[]) {
        const dir = join(this.config.targetDir, "inputs");
        const promises = inputTypes.map(async type => {
            const stream = createStreamAndLog(
                join(dir, `${type.name}.ts`)
            );
            new InputWriter(type, stream, this.config).write();
            await stream.end();
        });
        await Promise.all([
            ...promises,
            this.writeSimpleIndex(dir, inputTypes)
        ]);
    }
Example #6
Source File: Writer.ts    From graphql-ts-client with MIT License 6 votes vote down vote up
protected importType(type: GraphQLType) {
        if (this.imported) {
            throw new Error("Writer's importing has been terminated");
        }
        if (type instanceof GraphQLNonNull) {
            this.importType(type.ofType);
        } else if (type instanceof GraphQLList) {
            this.importType(type.ofType);
        } else if (type instanceof GraphQLInputObjectType) {
            this.importedTypes.add(type);
        } else if (type instanceof GraphQLEnumType) {
            this.importedTypes.add(type);
        } else if (type instanceof GraphQLScalarType && this.config.scalarTypeMap !== undefined) {
            const mappedType = this.config.scalarTypeMap[type.name];
            if (typeof mappedType == 'object') {
                const importSource = mappedType.importSource;
                let set = this.importedScalarTypes.get(importSource);
                if (set === undefined) {
                    set = new Set<string>();
                    this.importedScalarTypes.set(importSource, set);
                }
                set.add(mappedType.typeName);
            };
        }
    }
Example #7
Source File: isRequired.test.ts    From amplify-codegen with Apache License 2.0 6 votes vote down vote up
describe('isRequired', () => {
  const testObj = new GraphQLInputObjectType({
    name: 'Address',
    fields: {
      street: { type: GraphQLString },
      requiredInt: { type: new GraphQLNonNull(GraphQLInt) },
    },
  });

  it('should return false for null types', () => {
    expect(isRequired(testObj.getFields().street.type)).toEqual(false);
  });

  it('should return true for non null types', () => {
    expect(isRequired(testObj.getFields().requiredInt.type)).toEqual(true);
  });
});
Example #8
Source File: codeGeneration.ts    From amplify-codegen with Apache License 2.0 6 votes vote down vote up
function structDeclarationForInputObjectType(generator: CodeGenerator, type: GraphQLInputObjectType) {
  const interfaceName = type.name;
  interfaceDeclaration(
    generator,
    {
      interfaceName,
    },
    () => {
      const properties = propertiesFromFields(generator.context, Object.values(type.getFields()));
      properties.forEach(property => propertyDeclaration(generator, { ...property }));
    },
  );
}
Example #9
Source File: codeGeneration.ts    From amplify-codegen with Apache License 2.0 6 votes vote down vote up
function printEnumsAndInputObjects(generator: FlowAPIGenerator, context: CompilerContext) {
  generator.printer.enqueue(stripIndent`
    //==============================================================
    // START Enums and Input Objects
    // All enums and input objects are included in every output file
    // for now, but this will be changed soon.
    // TODO: Link to issue to fix this.
    //==============================================================
  `);

  context.typesUsed
    .filter(type => type instanceof GraphQLEnumType)
    .forEach(enumType => {
      generator.typeAliasForEnumType(enumType as GraphQLEnumType);
    });

  context.typesUsed
    .filter(type => type instanceof GraphQLInputObjectType)
    .forEach(inputObjectType => {
      generator.typeAliasForInputObjectType(inputObjectType as GraphQLInputObjectType);
    });

  generator.printer.enqueue(stripIndent`
    //==============================================================
    // END Enums and Input Objects
    //==============================================================
  `);
}
Example #10
Source File: language.ts    From amplify-codegen with Apache License 2.0 6 votes vote down vote up
public inputObjectDeclaration(inputObjectType: GraphQLInputObjectType) {
    const { name, description } = inputObjectType;

    const fieldMap = inputObjectType.getFields();
    const fields: ObjectProperty[] = Object.keys(inputObjectType.getFields()).map((fieldName: string) => {
      const field = fieldMap[fieldName];
      return {
        name: fieldName,
        annotation: this.typeAnnotationFromGraphQLType(field.type),
      };
    });

    const typeAlias = this.typeAliasObject(name, fields);

    typeAlias.leadingComments = [
      {
        type: 'CommentLine',
        value: ` ${description}`,
      } as t.CommentLine,
    ];

    return typeAlias;
  }
Example #11
Source File: serializeToJSON.ts    From amplify-codegen with Apache License 2.0 6 votes vote down vote up
function serializeInputObjectType(type: GraphQLInputObjectType) {
  const { name, description } = type;
  const fields = Object.values(type.getFields());

  return {
    kind: 'InputObjectType',
    name,
    description,
    fields: fields.map(field => ({
      name: field.name,
      type: String(field.type),
      description: field.description,
      defaultValue: field.defaultValue,
    })),
  };
}
Example #12
Source File: InputWriter.ts    From graphql-ts-client with MIT License 6 votes vote down vote up
protected importingBehavior(type: GraphQLNamedType): ImportingBehavior {
        if (type === this.inputType) {
            return "SELF";
        }
        if (type instanceof GraphQLInputObjectType) {
            return "SAME_DIR";
        }
        return "OTHER_DIR";
    }
Example #13
Source File: Writer.ts    From graphql-ts-client with MIT License 5 votes vote down vote up
write() {
        this.prepareImportings();
        this.imported = true;
        for (const importStatement of this.importStatements) {
            this.stream.write(importStatement);
            this.stream.write("\n");
        }
        for (const importedType of this.importedTypes) {
            const behavior = this.importingBehavior(importedType);
            if (behavior === 'SELF') {
                continue;
            }
            if (behavior === 'SAME_DIR') {
                this.stream.write(`import type {${importedType.name}} from '.';\n`);
            } else {
                let subDir: string;
                if (importedType instanceof GraphQLInputObjectType) {
                    subDir = "inputs"; 
                } else if (importedType instanceof GraphQLEnumType) {
                    subDir = "enums"; 
                } else {
                    subDir = "fetchers";
                }
                if (this.isUnderGlobalDir()) {
                    this.stream.write(`import type {${importedType.name}} from './${subDir}';\n`);    
                } else {
                    this.stream.write(`import type {${importedType.name}} from '../${subDir}';\n`);
                }
            }
        }
        if (this.importedScalarTypes.size !== 0) {
            const sourcePrefix = this.isUnderGlobalDir() ? "../" : "../../";
            for (const [importSource, typeNames] of this.importedScalarTypes) {
                this.stream.write(`import type { ${Array.from(typeNames).join(", ")} } from '${sourcePrefix}${importSource}';\n`);
            }
        }
        if (this.importStatements.size !== 0 || this.importedTypes.size !== 0 || this.importedScalarTypes.size !== 0) {
            this.stream.write("\n");
        }
        this.writeCode();
    }
Example #14
Source File: InputWriter.ts    From graphql-ts-client with MIT License 5 votes vote down vote up
constructor(
        private readonly inputType: GraphQLInputObjectType,
        stream: WriteStream,
        config: GeneratorConfig
    ) {
        super(stream, config);
    }
Example #15
Source File: codeGeneration.ts    From amplify-codegen with Apache License 2.0 5 votes vote down vote up
structDeclarationForInputObjectType(type: GraphQLInputObjectType) {
    const { name: structName, description } = type;
    const adoptedProtocols = ['GraphQLMapConvertible'];
    const fields = Object.values(type.getFields());

    let properties = fields.map(this.helpers.propertyFromInputField, this.helpers);

    // File input should have localUri and mimeType which is used only in client
    if (isS3Field(type)) {
      properties = [
        ...properties,
        ...(properties.find(p => p.name === 'localUri')
          ? []
          : [
              {
                propertyName: 'localUri',
                name: 'localUri',
                typeName: 'String',
                isOptional: false,
                description: '',
              },
            ]),
        ...(properties.find(p => p.name === 'mimeType')
          ? []
          : [
              {
                propertyName: 'mimeType',
                name: 'mimeType',
                typeName: 'String',
                isOptional: false,
                description: '',
              },
            ]),
      ];
    }

    this.structDeclaration({ structName, description: description || undefined, adoptedProtocols }, () => {
      this.printOnNewline(`public var graphQLMap: GraphQLMap`);

      this.printNewlineIfNeeded();
      this.printOnNewline(`public init`);
      this.print('(');
      this.print(
        join(
          properties.map(({ propertyName, typeName, isOptional }) =>
            join([`${escapeIdentifierIfNeeded(propertyName)}: ${typeName}`, isOptional && ' = nil'])
          ),
          ', '
        )
      );
      this.print(')');

      this.withinBlock(() => {
        this.printOnNewline(
          wrap(
            `graphQLMap = [`,
            join(properties.map(({ name, propertyName }) => `"${name}": ${escapeIdentifierIfNeeded(propertyName)}`), ', ') || ':',
            `]`
          )
        );
      });

      for (const { name, propertyName, typeName, description } of properties) {
        this.printNewlineIfNeeded();
        this.comment(description || undefined);
        this.printOnNewline(`public var ${escapeIdentifierIfNeeded(propertyName)}: ${typeName}`);
        this.withinBlock(() => {
          this.printOnNewline('get');
          this.withinBlock(() => {
            this.printOnNewline(`return graphQLMap["${name}"] as! ${typeName}`);
          });
          this.printOnNewline('set');
          this.withinBlock(() => {
            this.printOnNewline(`graphQLMap.updateValue(newValue, forKey: "${name}")`);
          });
        });
      }
    });
  }
Example #16
Source File: Writer.ts    From graphql-ts-client with MIT License 5 votes vote down vote up
protected typeRef(
        type: GraphQLType,
        objectRender?: string | ((type: GraphQLObjectType | GraphQLInterfaceType, field: GraphQLField<any, any>) => boolean)
    ) {
        if (type instanceof GraphQLScalarType) {
            const mappedType = 
                (this.config.scalarTypeMap ?? EMPTY_MAP)[type.name] 
                ?? SCALAR_MAP[type.name];
            if (mappedType === undefined) {
                throw new Error(`Unknown scalar type ${type.name}`);
            }
            if (typeof mappedType === 'string') {
                this.text(mappedType);
            } else {
                this.text(mappedType.typeName);
            }
        } else if (type instanceof GraphQLObjectType || 
            type instanceof GraphQLInterfaceType ||
            type instanceof GraphQLUnionType
        ) {
            if (typeof objectRender === "string") {
                this.text(objectRender);
            } else if (type instanceof GraphQLUnionType) {
                this.enter("BLANK");
                for (const itemType of type.getTypes()) {
                    this.separator(" | ");
                    this.text(itemType.name);    
                }
                this.leave();
            } else if (typeof objectRender === 'function') {
                this.scope({type: "BLOCK", multiLines: true}, () => {
                    const fieldMap = type.getFields();
                    for (const fieldName in fieldMap) {
                        const field = fieldMap[fieldName];
                        if (objectRender(type, field)) {
                            this.separator(", ");
                            this.text("readonly ");
                            this.text(fieldName);
                            this.text(": ");
                            this.typeRef(field.type, objectRender);
                        }
                    }
                });
            } else {
                this.text(type.name);
            }
        } else if (type instanceof GraphQLEnumType || type instanceof GraphQLInputObjectType) {
            this.text(type.name);
        } else if (type instanceof GraphQLNonNull) {
            this.typeRef(type.ofType, objectRender);
        } else if (type instanceof GraphQLList) {
            if (type.ofType instanceof GraphQLNonNull) {
                if (!this.config.arrayEditable) {
                    this.text("readonly ");
                }
                this.typeRef(type.ofType, objectRender);
                this.text("[]");
            } else {
                if (!this.config.arrayEditable) {
                    this.text("Readonly");
                }
                this.text("Array<");
                this.typeRef(type.ofType, objectRender);
                this.text(" | undefined>");
            }
        }
    }
Example #17
Source File: buildWhereInputType.ts    From payload with MIT License 5 votes vote down vote up
buildWhereInputType = (name: string, fields: Field[], parentName: string): GraphQLInputObjectType => {
  // This is the function that builds nested paths for all
  // field types with nested paths.

  const fieldTypes = fields.reduce((schema, field) => {
    if (!fieldIsPresentationalOnly(field) && !field.hidden) {
      const getFieldSchema = fieldToSchemaMap(parentName)[field.type];

      if (getFieldSchema) {
        const fieldSchema = getFieldSchema(field);

        if (fieldHasSubFields(field)) {
          return {
            ...schema,
            ...(fieldSchema.reduce((subFields, subField) => ({
              ...subFields,
              [formatName(subField.key)]: subField.type,
            }), {})),
          };
        }

        return {
          ...schema,
          [formatName(field.name)]: fieldSchema,
        };
      }
    }

    return schema;
  }, {});

  fieldTypes.id = {
    type: withOperators(
      { name: 'id' } as FieldAffectingData,
      GraphQLJSON,
      parentName,
      [...operators.equality, ...operators.contains],
    ),
  };

  const fieldName = formatName(name);

  return new GraphQLInputObjectType({
    name: `${fieldName}_where`,
    fields: {
      ...fieldTypes,
      OR: {
        type: new GraphQLList(new GraphQLInputObjectType({
          name: `${fieldName}_where_or`,
          fields: fieldTypes,
        })),
      },
      AND: {
        type: new GraphQLList(new GraphQLInputObjectType({
          name: `${fieldName}_where_and`,
          fields: fieldTypes,
        })),
      },
    },
  });
}
Example #18
Source File: InputWriter.d.ts    From graphql-ts-client with MIT License 5 votes vote down vote up
constructor(inputType: GraphQLInputObjectType, stream: WriteStream, config: GeneratorConfig);
Example #19
Source File: codeGeneration.ts    From amplify-codegen with Apache License 2.0 5 votes vote down vote up
typeDeclarationForGraphQLType(type: GraphQLType) {
    if (type instanceof GraphQLEnumType) {
      this.enumerationDeclaration(type);
    } else if (type instanceof GraphQLInputObjectType) {
      this.structDeclarationForInputObjectType(type);
    }
  }
Example #20
Source File: codeGeneration.ts    From amplify-codegen with Apache License 2.0 5 votes vote down vote up
public typeAliasForInputObjectType(inputObjectType: GraphQLInputObjectType) {
    this.printer.enqueue(this.inputObjectDeclaration(inputObjectType));
  }
Example #21
Source File: objectType.ts    From genql with MIT License 5 votes vote down vote up
objectType = (
    type: GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType,
    ctx: RenderContext,
) => {
    const typeObj: FieldMap<string> = Object.keys(type.getFields()).reduce<
        FieldMap<string>
    >((r, f) => {
        const field = type.getFields()[f]
        const namedType = getNamedType(field.type)
        const fieldObj: Field<string> = { type: namedType.name }
        r[f] = fieldObj

        const args: GraphQLArgument[] =
            (<GraphQLField<any, any>>field).args || []

        if (args.length > 0) {
            fieldObj.args = args.reduce<ArgMap<string>>((r, a) => {
                const concreteType = a.type.toString()
                const typename = getNamedType(a.type).name
                r[a.name] = [typename]
                if (typename !== concreteType) {
                    r[a.name]?.push(concreteType)
                }
                return r
            }, {})
        }

        return r
    }, {})

    if (isInterfaceType(type) && ctx.schema) {
        ctx.schema.getPossibleTypes(type).map((t) => {
            if (!isEmpty(typeObj)) {
                typeObj[`on_${t.name}`] = { type: t.name }
            }
        })
    }

    if (!isEmpty(typeObj)) {
        typeObj.__typename = { type: 'String' }
    }

    // const scalar = Object.keys(type.getFields())
    //   .map(f => type.getFields()[f])
    //   .filter(f => isScalarType(getNamedType(f.type)) || isEnumType(getNamedType(f.type)))
    //   .map(f => f.name)

    // if (scalar.length > 0) typeObj.scalar = scalar

    return typeObj
}
Example #22
Source File: schema-resolver.ts    From graphql-mesh with MIT License 5 votes vote down vote up
private createObjectType(soapType: SoapObjectType): GraphQLInputObjectType {
    return new GraphQLInputObjectType(this.createObjectTypeConfig(soapType));
  }
Example #23
Source File: example_api.test.ts    From graphql-mesh with MIT License 5 votes vote down vote up
test('Required properties for input object types', () => {
  const userInputType = createdSchema.getType('UserInput') as GraphQLInputObjectType;

  // The exclamation mark shows that it is a required (non-nullable) property
  expect(userInputType.toConfig().fields.address.type.toString()).toEqual('AddressInput!');
  expect(userInputType.toConfig().fields.address2.type.toString()).toEqual('AddressInput');
});
Example #24
Source File: types.ts    From tql with MIT License 4 votes vote down vote up
transform = (
  ast: DocumentNode,
  schema: GraphQLSchema
): ASTVisitor => {
  // @note needed to serialize inline enum values correctly at runtime
  const enumValues = new Set<string>();

  return {
    [Kind.DIRECTIVE_DEFINITION]: () => null,

    [Kind.SCALAR_TYPE_DEFINITION]: () => null,

    [Kind.ENUM_TYPE_DEFINITION]: (node) => {
      const typename = node.name.value;
      const values = node.values?.map((v) => v.name.value) ?? [];

      const printMember = (member: string): string => {
        return `${member} = "${member}"`;
      };

      return code`
        export enum ${typename} {
          ${values.map(printMember).join(",\n")}
        }
      `;
    },

    [Kind.ENUM_VALUE_DEFINITION]: (node) => {
      enumValues.add(node.name.value);
      return null;
    },

    [Kind.INPUT_OBJECT_TYPE_DEFINITION]: (node) => {
      const typename = node.name.value;
      const type = schema.getType(typename);

      invariant(
        type instanceof GraphQLInputObjectType,
        `Type "${typename}" was not instance of expected class GraphQLInputObjectType.`
      );

      const fields = Object.values(type.getFields());

      const printField = (field: GraphQLInputField) => {
        const isList = listType(field.type);
        const isNonNull = isNonNullType(field.type);
        const baseType = inputType(field.type);

        let tsType: string;

        if (baseType instanceof GraphQLScalarType) {
          tsType = toPrimitive(baseType);
        } else if (baseType instanceof GraphQLEnumType) {
          tsType = baseType.name;
        } else if (baseType instanceof GraphQLInputObjectType) {
          tsType = "I" + baseType.name;
        } else {
          throw new Error("Unable to render inputField!");
        }

        return [
          field.name,
          isNonNull ? ":" : "?:",
          " ",
          tsType,
          isList ? "[]" : "",
        ].join("");
      };

      return code`
        export interface I${typename} {
          ${fields.map(printField).join("\n")}
        }
      `;
    },

    [Kind.OBJECT_TYPE_DEFINITION]: (node) => {
      const typename = node.name.value;
      const type = schema.getType(typename);

      invariant(
        type instanceof GraphQLObjectType,
        `Type "${typename}" was not instance of expected class GraphQLObjectType.`
      );

      const fields = Object.values(type.getFields());
      const interfaces = type.getInterfaces();

      // @note TypeScript only requires new fields to be defined on interface extendors
      const interfaceFields = interfaces.flatMap((i) =>
        Object.values(i.getFields()).map((field) => field.name)
      );
      const uncommonFields = fields.filter(
        (field) => !interfaceFields.includes(field.name)
      );

      // @todo extend any implemented interfaces
      // @todo only render fields unique to this type
      const extensions =
        interfaces.length > 0
          ? `extends ${interfaces.map((i) => "I" + i.name).join(", ")}`
          : "";

      return code`
        export interface I${typename} ${extensions} {
          readonly __typename: ${`"${typename}"`}
          ${uncommonFields.map(printField).join("\n")}
        }
      `;
    },

    [Kind.INTERFACE_TYPE_DEFINITION]: (node) => {
      const typename = node.name.value;
      const type = schema.getType(typename);

      invariant(
        type instanceof GraphQLInterfaceType,
        `Type "${typename}" was not instance of expected class GraphQLInterfaceType.`
      );

      // @note Get all implementors of this union
      const implementations = schema
        .getPossibleTypes(type)
        .map((type) => type.name);

      const fields = Object.values(type.getFields());

      return code`
        export interface I${typename} {
          readonly __typename: ${implementations
            .map((type) => `"${type}"`)
            .join(" | ")}
          ${fields.map(printField).join("\n")}
        }
      `;
    },

    [Kind.UNION_TYPE_DEFINITION]: (node) => {
      const typename = node.name.value;
      const type = schema.getType(typename);

      invariant(
        type instanceof GraphQLUnionType,
        `Type "${typename}" was not instance of expected class GraphQLUnionType.`
      );

      // @note Get all implementors of this union
      const implementations = schema
        .getPossibleTypes(type)
        .map((type) => type.name);

      return code`
        export type ${"I" + type.name} = ${implementations
        .map((type) => `I${type}`)
        .join(" | ")}
      `;
    },

    [Kind.SCHEMA_DEFINITION]: (_) => {
      return null;
    },
  };
}
Example #25
Source File: fieldToWhereInputSchemaMap.ts    From payload with MIT License 4 votes vote down vote up
fieldToSchemaMap: (parentName: string) => any = (parentName: string) => ({
  number: (field: NumberField) => {
    const type = GraphQLFloat;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, ...operators.comparison],
      ),
    };
  },
  text: (field: TextField) => {
    const type = GraphQLString;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  email: (field: EmailField) => {
    const type = EmailAddressResolver;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  textarea: (field: TextareaField) => {
    const type = GraphQLString;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  richText: (field: RichTextField) => {
    const type = GraphQLJSON;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  code: (field: CodeField) => {
    const type = GraphQLString;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  radio: (field: RadioField) => ({
    type: withOperators(
      field,
      new GraphQLEnumType({
        name: `${combineParentName(parentName, field.name)}_Input`,
        values: field.options.reduce((values, option) => {
          if (optionIsObject(option)) {
            return {
              ...values,
              [formatName(option.value)]: {
                value: option.value,
              },
            };
          }

          return {
            ...values,
            [formatName(option)]: {
              value: option,
            },
          };
        }, {}),
      }),
      parentName,
      [...operators.equality, 'like', 'contains'],
    ),
  }),
  date: (field: DateField) => {
    const type = DateTimeResolver;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, ...operators.comparison, 'like'],
      ),
    };
  },
  point: (field: PointField) => {
    const type = GraphQLList(GraphQLFloat);
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, ...operators.comparison, ...operators.geo],
      ),
    };
  },
  relationship: (field: RelationshipField) => {
    let type = withOperators(
      field,
      GraphQLString,
      parentName,
      [...operators.equality, ...operators.contains],
    );

    if (Array.isArray(field.relationTo)) {
      type = new GraphQLInputObjectType({
        name: `${combineParentName(parentName, field.name)}_Relation`,
        fields: {
          relationTo: {
            type: new GraphQLEnumType({
              name: `${combineParentName(parentName, field.name)}_Relation_RelationTo`,
              values: field.relationTo.reduce((values, relation) => ({
                ...values,
                [formatName(relation)]: {
                  value: relation,
                },
              }), {}),
            }),
          },
          value: { type: GraphQLString },
        },
      });
    }

    return { type };
  },
  upload: (field: UploadField) => ({
    type: withOperators(
      field,
      GraphQLString,
      parentName,
      [...operators.equality],
    ),
  }),
  checkbox: (field: CheckboxField) => ({
    type: withOperators(
      field,
      GraphQLBoolean,
      parentName,
      [...operators.equality],
    ),
  }),
  select: (field: SelectField) => ({
    type: withOperators(
      field,
      new GraphQLEnumType({
        name: `${combineParentName(parentName, field.name)}_Input`,
        values: field.options.reduce((values, option) => {
          if (typeof option === 'object' && option.value) {
            return {
              ...values,
              [formatName(option.value)]: {
                value: option.value,
              },
            };
          }

          if (typeof option === 'string') {
            return {
              ...values,
              [option]: {
                value: option,
              },
            };
          }

          return values;
        }, {}),
      }),
      parentName,
      [...operators.equality, ...operators.contains],
    ),
  }),
  array: (field: ArrayField) => recursivelyBuildNestedPaths(parentName, field),
  group: (field: GroupField) => recursivelyBuildNestedPaths(parentName, field),
  row: (field: RowField) => field.fields.reduce((rowSchema, rowField) => {
    const getFieldSchema = fieldToSchemaMap(parentName)[rowField.type];

    if (getFieldSchema) {
      const rowFieldSchema = getFieldSchema(rowField);

      if (fieldHasSubFields(rowField)) {
        return [
          ...rowSchema,
          ...rowFieldSchema,
        ];
      }

      if (fieldAffectsData(rowField)) {
        return [
          ...rowSchema,
          {
            key: rowField.name,
            type: rowFieldSchema,
          },
        ];
      }
    }


    return rowSchema;
  }, []),
})
Example #26
Source File: generateSchema.ts    From davinci with MIT License 4 votes vote down vote up
generateGQLSchema = ({
	type,
	parentType,
	key,
	schemas = {},
	isInput = false,
	operationType,
	resolverMetadata,
	partial,
	transformMetadata = _.identity
}: IGenerateGQLSchemaArgs) => {
	// grab meta infos
	// maybe it's a decorated class, let's try to get the fields metadata
	const parentFieldsMetadata: IFieldDecoratorMetadata[] = parentType?.prototype
		? getFieldsMetadata(parentType.prototype.constructor, isInput, operationType, resolverMetadata)
		: [];
	const meta = _fp.find({ key }, parentFieldsMetadata) || ({} as IFieldDecoratorMetadata);
	const metadata = transformMetadata(meta, { isInput, type, parentType, schemas });
	const isRequired = !partial && metadata.opts?.required;

	// it's a primitive type, simple case
	if ([String, Number, Boolean, Date, Object].includes(type)) {
		const gqlType = scalarDict[type.name.toLowerCase()];
		const schema = isRequired ? new GraphQLNonNull(gqlType) : gqlType;
		return { schema, schemas };
	}

	// it's an array => recursively call makeSchema on the first array element
	if (Array.isArray(type)) {
		const gqlSchema = generateGQLSchema({
			type: type[0],
			parentType,
			schemas,
			key,
			isInput,
			operationType,
			resolverMetadata,
			partial,
			transformMetadata
		});
		const gqlType = new GraphQLList(gqlSchema.schema);
		const schema = isRequired ? new GraphQLNonNull(gqlType) : gqlType;

		return { schema, schemas: _.merge(schemas, gqlSchema.schemas) };
	}

	// it's a complex type => create nested types
	if (typeof type === 'function' || typeof type === 'object') {
		const suffix = isInput
			? _.compact([partial && 'Partial', _.upperFirst(operationType || ''), 'Input']).join('')
			: '';
		const typeMetadata = Reflector.getMetadata('davinci:graphql:types', type) || {};
		const name = `${typeMetadata.name || type.name || key}${suffix}`;

		// existing type, let's return it
		if (schemas[name]) {
			return { schema: schemas[name], schemas };
		}

		if (type instanceof UnionType) {
			const types = Array.isArray(type.types)
				? type.types.map(
					t =>
						generateGQLSchema({
							type: t,
							parentType,
							schemas,
							key,
							isInput,
							operationType,
							resolverMetadata,
							partial,
							transformMetadata
						}).schema
				  )
				: type.types;

			const unionTypeConfig = {
				..._.omit(metadata.opts, ['type']),
				name,
				types,
				resolveType: type.resolveType
			};

			schemas[name] = isRequired
				? new GraphQLNonNull(new GraphQLUnionType(unionTypeConfig))
				: new GraphQLUnionType(unionTypeConfig);
		} else {
			const objTypeConfig: any = {
				...metadata.opts,
				name,

				// eslint-disable-next-line no-use-before-define
				fields: createObjectFields({
					parentType: type,
					schemas,
					isInput,
					operationType,
					resolverMetadata,
					partial,
					transformMetadata
				})
			};

			const ObjectType = isInput ? GraphQLInputObjectType : GraphQLObjectType;
			schemas[name] = isRequired
				? new GraphQLNonNull(new ObjectType(objTypeConfig))
				: new ObjectType(objTypeConfig);
		}

		return { schema: schemas[name], schemas };
	}

	return null;
}
Example #27
Source File: buildMutationInputType.ts    From payload with MIT License 4 votes vote down vote up
function buildMutationInputType(name: string, fields: Field[], parentName: string, forceNullable = false): GraphQLInputObjectType {
  const fieldToSchemaMap = {
    number: (field: NumberField) => {
      const type = field.name === 'id' ? GraphQLInt : GraphQLFloat;
      return { type: withNullableType(field, type, forceNullable) };
    },
    text: (field: TextField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    email: (field: EmailField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    textarea: (field: TextareaField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    richText: (field: RichTextField) => ({ type: withNullableType(field, GraphQLJSON, forceNullable) }),
    code: (field: CodeField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    date: (field: DateField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    upload: (field: UploadField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    radio: (field: RadioField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    point: (field: PointField) => ({ type: withNullableType(field, GraphQLList(GraphQLFloat), forceNullable) }),
    checkbox: () => ({ type: GraphQLBoolean }),
    select: (field: SelectField) => {
      const formattedName = `${combineParentName(parentName, field.name)}_MutationInput`;
      let type: GraphQLType = new GraphQLEnumType({
        name: formattedName,
        values: field.options.reduce((values, option) => {
          if (typeof option === 'object' && option.value) {
            return {
              ...values,
              [formatName(option.value)]: {
                value: option.value,
              },
            };
          }

          if (typeof option === 'string') {
            return {
              ...values,
              [option]: {
                value: option,
              },
            };
          }

          return values;
        }, {}),
      });

      type = field.hasMany ? new GraphQLList(type) : type;
      type = withNullableType(field, type, forceNullable);

      return { type };
    },
    relationship: (field: RelationshipField) => {
      const { relationTo } = field;
      type PayloadGraphQLRelationshipType = GraphQLScalarType | GraphQLList<GraphQLScalarType> | GraphQLInputObjectType;
      let type: PayloadGraphQLRelationshipType;

      if (Array.isArray(relationTo)) {
        const fullName = `${combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label)}RelationshipInput`;
        type = new GraphQLInputObjectType({
          name: fullName,
          fields: {
            relationTo: {
              type: new GraphQLEnumType({
                name: `${fullName}RelationTo`,
                values: relationTo.reduce((values, option) => ({
                  ...values,
                  [formatName(option)]: {
                    value: option,
                  },
                }), {}),
              }),
            },
            value: { type: GraphQLJSON },
          },
        });
      } else {
        type = getCollectionIDType(payload.collections[relationTo].config);
      }

      return { type: field.hasMany ? new GraphQLList(type) : type };
    },
    array: (field: ArrayField) => {
      const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
      let type: GraphQLType | GraphQLList<GraphQLType> = buildMutationInputType(fullName, field.fields, fullName);
      type = new GraphQLList(withNullableType(field, type, forceNullable));
      return { type };
    },
    group: (field: GroupField) => {
      const requiresAtLeastOneField = field.fields.some((subField) => (!fieldIsPresentationalOnly(subField) && subField.required && !subField.localized));
      const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
      let type: GraphQLType = buildMutationInputType(fullName, field.fields, fullName);
      if (requiresAtLeastOneField) type = new GraphQLNonNull(type);
      return { type };
    },
    blocks: () => ({ type: GraphQLJSON }),
    row: (field: RowField) => field.fields.reduce((acc, rowField: RowField) => {
      const getFieldSchema = fieldToSchemaMap[rowField.type];

      if (getFieldSchema) {
        const fieldSchema = getFieldSchema(rowField);

        return [
          ...acc,
          fieldSchema,
        ];
      }

      return acc;
    }, []),
  };

  const fieldTypes = fields.reduce((schema, field: Field) => {
    if (!fieldIsPresentationalOnly(field) && !field.hidden) {
      const getFieldSchema: (field: Field) => { type: GraphQLType } = fieldToSchemaMap[field.type];

      if (getFieldSchema) {
        const fieldSchema = getFieldSchema(field);

        if (fieldHasSubFields(field) && Array.isArray(fieldSchema)) {
          return fieldSchema.reduce((acc, subField, i) => {
            const currentSubField = field.fields[i];
            if (fieldAffectsData(currentSubField)) {
              return {
                ...acc,
                [currentSubField.name]: subField,
              };
            }

            return {
              ...acc,
              ...fieldSchema,
            };
          }, schema);
        }

        if (fieldAffectsData(field)) {
          return {
            ...schema,
            [field.name]: fieldSchema,
          };
        }

        return {
          ...schema,
          ...fieldSchema,
        };
      }
    }

    return schema;
  }, {});

  const fieldName = formatName(name);

  return new GraphQLInputObjectType({
    name: `mutation${fieldName}Input`,
    fields: {
      ...fieldTypes,
    },
  });
}
Example #28
Source File: schema.ts    From Deep-Lynx with MIT License 4 votes vote down vote up
inputFieldsForTransformation(transformation: TypeTransformation): {[key: string]: any} {
        const fields: {[key: string]: any} = {};

        transformation.keys?.forEach((key) => {
            if (!key.column_name || !key.value_type) return;

            const propertyName = stringToValidPropertyName(key.column_name);

            switch (key.value_type) {
                // because we have no specification on our internal number type, we
                // must set this as a float for now
                case 'number': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLInt)},
                            },
                        }),
                    };
                    break;
                }

                case 'float': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLFloat)},
                            },
                        }),
                    };
                    break;
                }

                case 'boolean': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLBoolean)},
                            },
                        }),
                    };
                    break;
                }

                case 'string' || 'date' || 'file': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLString)},
                            },
                        }),
                    };
                    break;
                }

                case 'list': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLJSON)},
                            },
                        }),
                    };
                    break;
                }

                default: {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLString)},
                            },
                        }),
                    };
                }
            }
        });

        return fields;
    }
Example #29
Source File: schema.ts    From Deep-Lynx with MIT License 4 votes vote down vote up
// generate requires a containerID because the schema it generates is based on a user's ontology and ontologies are
    // separated by containers
    async ForContainer(containerID: string, options: ResolverOptions): Promise<Result<GraphQLSchema>> {
        // fetch the currently published ontology if the versionID wasn't provided
        if (!options.ontologyVersionID) {
            const ontResults = await this.#ontologyRepo
                .where()
                .containerID('eq', containerID)
                .and()
                .status('eq', 'published')
                .list({sortBy: 'id', sortDesc: true});
            if (ontResults.isError || ontResults.value.length === 0) {
                Logger.error('unable to fetch current ontology, or no currently published ontology');
            } else {
                options.ontologyVersionID = ontResults.value[0].id;
            }
        }

        // fetch all metatypes for the container, with their keys - the single most expensive call of this function
        const metatypeResults = await this.#metatypeRepo
            .where()
            .containerID('eq', containerID)
            .and()
            .ontologyVersion('eq', options.ontologyVersionID)
            .list(true);
        if (metatypeResults.isError) {
            return Promise.resolve(Result.Pass(metatypeResults));
        }

        // fetch all metatype relationship pairs - used for _relationship queries.
        const metatypePairResults = await this.#metatypePairRepo
            .where()
            .containerID('eq', containerID)
            .and()
            .ontologyVersion('eq', options.ontologyVersionID)
            .list();
        if (metatypePairResults.isError) {
            return Promise.resolve(Result.Pass(metatypePairResults));
        }

        // fetch all relationship types. Used for relationship wrapper queries.
        const relationshipResults = await this.#relationshipRepo
            .where()
            .containerID('eq', containerID)
            .and()
            .ontologyVersion('eq', options.ontologyVersionID)
            .list(true);
        if (relationshipResults.isError) {
            return Promise.resolve(Result.Pass(relationshipResults));
        }

        // used for querying edges based on node (see input._relationship resolver)
        const metatypePairObjects: {[key: string]: any} = {};
        metatypePairResults.value.forEach((pair) => {
            const origin = stringToValidPropertyName(pair.origin_metatype_name!);
            const rel = stringToValidPropertyName(pair.relationship_name!);
            const dest = stringToValidPropertyName(pair.destination_metatype_name!);
            // populate list for forward searching
            if (!(origin in metatypePairObjects)) {
                metatypePairObjects[origin] = {};
            }
            if (!(rel in metatypePairObjects[origin])) {
                metatypePairObjects[origin][rel] = {};
            }
            if (!(dest in metatypePairObjects[origin][rel])) {
                metatypePairObjects[origin][rel][dest] = {type: GraphQLString};
            }
        });

        const metatypeGraphQLObjects: {[key: string]: any} = {};

        // we must declare the metadata input object beforehand so we can include it in the final schema entry for each
        // metatype
        const recordInputType = new GraphQLInputObjectType({
            name: 'record_input',
            fields: {
                id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                original_id: {type: GraphQLJSON}, // since the original ID might be a number, treat it as valid JSON
                import_id: {type: GraphQLString},
                limit: {type: GraphQLInt, defaultValue: 10000},
                page: {type: GraphQLInt, defaultValue: 1},
            },
        });

        const recordInfo = new GraphQLObjectType({
            name: 'recordInfo',
            fields: {
                id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                original_id: {type: GraphQLJSON}, // since the original ID might be a number, treat it as valid JSON
                import_id: {type: GraphQLString},
                metatype_id: {type: GraphQLString},
                metatype_name: {type: GraphQLString},
                created_at: {type: GraphQLString},
                created_by: {type: GraphQLString},
                modified_at: {type: GraphQLString},
                modified_by: {type: GraphQLString},
                metadata: {type: GraphQLJSON},
                count: {type: GraphQLInt},
                page: {type: GraphQLInt},
            },
        });

        // needed when a user decides they want the results as a file vs. a raw return
        const fileInfo = new GraphQLObjectType({
            name: 'fileInfo',
            fields: {
                id: {type: GraphQLString},
                file_name: {type: GraphQLString},
                file_size: {type: GraphQLFloat},
                md5hash: {type: GraphQLString},
                metadata: {type: GraphQLJSON},
                url: {type: GraphQLString},
            },
        });

        metatypeResults.value.forEach((metatype) => {
            if (!metatype.keys || metatype.keys.length === 0) return;

            // the following 4 input/object types are used for querying or introspection on _relationship
            const destinationInputType = new GraphQLInputObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_destination_input`,
                // needed because the return type accepts an object, but throws a fit about it
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: {[key: string]: any}} = {};
                    if (metatypePairObjects[stringToValidPropertyName(metatype.name)]) {
                        Object.keys(metatypePairObjects[stringToValidPropertyName(metatype.name)]).forEach((pair) => {
                            Object.keys(metatypePairObjects[stringToValidPropertyName(metatype.name)][pair]).forEach((dest) => {
                                fields[dest] = {type: GraphQLBoolean};
                            });
                        });
                    }
                    return fields;
                },
            });

            const relationshipInputType = new GraphQLInputObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_relationship_input`,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: {[key: string]: GraphQLNamedType | GraphQLList<any>}} = {};
                    if (metatypePairObjects[metatype.name]) {
                        Object.keys(metatypePairObjects[metatype.name]).forEach((rel) => {
                            fields[rel] = {type: new GraphQLList(destinationInputType)};
                        });
                    } else {
                        // if no relationships exists, set relationship to _none: true
                        fields._none = {type: GraphQLBoolean};
                    }
                    return fields;
                },
            });

            const destinationInfo = new GraphQLObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_destinationInfo`,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: any} = {};
                    if (metatypePairObjects[metatype.name]) {
                        Object.keys(metatypePairObjects[metatype.name]).forEach((pair) => {
                            Object.keys(metatypePairObjects[metatype.name][pair]).forEach((dest) => {
                                fields[dest] = {type: GraphQLString};
                            });
                        });
                    }
                    return fields;
                },
            });

            const relationshipInfo = new GraphQLObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_relationshipInfo`,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: any} = {};
                    if (metatypePairObjects[metatype.name]) {
                        Object.keys(metatypePairObjects[metatype.name]).forEach((pair) => {
                            fields[pair] = {type: destinationInfo};
                        });
                    } else {
                        // if no relationships exists, set relationship to _none: true
                        fields._none = {type: GraphQLBoolean};
                    }
                    return fields;
                },
            });

            metatypeGraphQLObjects[stringToValidPropertyName(metatype.name)] = {
                args: {
                    ...this.inputFieldsForMetatype(metatype),
                    _record: {type: recordInputType},
                    _relationship: {type: relationshipInputType},
                },
                description: metatype.description,
                type: options.returnFile
                    ? fileInfo
                    : new GraphQLList(
                        new GraphQLObjectType({
                            name: stringToValidPropertyName(metatype.name),
                            // needed because the return type accepts an object, but throws a fit about it
                            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                            // @ts-ignore
                            fields: () => {
                                const output: {[key: string]: {[key: string]: GraphQLNamedType | GraphQLList<any>}} = {};
                                output._record = {type: recordInfo};
                                output._relationship = {type: relationshipInfo};
                                output._file = {type: fileInfo};

                                metatype.keys?.forEach((metatypeKey) => {
                                    // keys must match the regex format of /^[_a-zA-Z][_a-zA-Z0-9]*$/ in order to be considered
                                    // valid graphql property names. While we force the user to meet these requirements at key
                                    // creation, we can't guarantee that legacy data will conform to these standards
                                    const propertyName = stringToValidPropertyName(metatypeKey.property_name);

                                    switch (metatypeKey.data_type) {
                                        // because we have no specification on our internal number type, we
                                        // must set this as a float for now
                                        case 'number': {
                                            output[propertyName] = {
                                                type: GraphQLFloat,
                                            };
                                            break;
                                        }

                                        case 'boolean': {
                                            output[propertyName] = {
                                                type: GraphQLBoolean,
                                            };
                                            break;
                                        }

                                        case 'string' || 'date' || 'file': {
                                            output[propertyName] = {
                                                type: GraphQLString,
                                            };
                                            break;
                                        }

                                        case 'list': {
                                            output[propertyName] = {
                                                type: new GraphQLList(GraphQLJSON),
                                            };
                                            break;
                                        }

                                        case 'enumeration': {
                                            const enumMap: {[key: string]: GraphQLEnumValueConfig} = {};

                                            if (metatypeKey.options) {
                                                metatypeKey.options.forEach((option) => {
                                                    enumMap[option] = {
                                                        value: option,
                                                    };
                                                });
                                            }

                                            output[propertyName] = {
                                                type: new GraphQLEnumType({
                                                    name: stringToValidPropertyName(`${metatype.name}_${metatypeKey.name}_Enum_TypeA`),
                                                    values: enumMap,
                                                }),
                                            };
                                            break;
                                        }

                                        default: {
                                            output[propertyName] = {
                                                type: GraphQLString,
                                            };
                                        }
                                    }
                                });

                                return output;
                            },
                        }),
                    ),
                resolve: this.resolverForMetatype(containerID, metatype, options),
            };
        });

        const relationshipGraphQLObjects: {[key: string]: any} = {};

        // metadata objects for edges (metatype relationships)
        const edgeRecordInputType = new GraphQLInputObjectType({
            name: 'edge_record_input',
            fields: {
                id: {type: GraphQLString},
                pair_id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                import_id: {type: GraphQLString},
                origin_id: {type: GraphQLString},
                destination_id: {type: GraphQLString},
                limit: {type: GraphQLInt, defaultValue: 10000},
                page: {type: GraphQLInt, defaultValue: 1},
            },
        });

        const edgeRecordInfo = new GraphQLObjectType({
            name: 'edge_recordInfo',
            fields: {
                id: {type: GraphQLString},
                pair_id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                import_id: {type: GraphQLString},
                origin_id: {type: GraphQLString},
                origin_metatype_id: {type: GraphQLString},
                destination_id: {type: GraphQLString},
                destination_metatype_id: {type: GraphQLString},
                relationship_name: {type: GraphQLString},
                created_at: {type: GraphQLString},
                created_by: {type: GraphQLString},
                modified_at: {type: GraphQLString},
                modified_by: {type: GraphQLString},
                metadata: {type: GraphQLJSON},
                count: {type: GraphQLInt},
                page: {type: GraphQLInt},
            },
        });

        relationshipResults.value.forEach((relationship) => {
            relationshipGraphQLObjects[stringToValidPropertyName(relationship.name)] = {
                args: {
                    ...this.inputFieldsForRelationship(relationship),
                    _record: {type: edgeRecordInputType},
                },
                description: relationship.description,
                type: (options.returnFile) ? fileInfo : new GraphQLList(
                    new GraphQLObjectType({
                        name: stringToValidPropertyName(relationship.name),
                        // needed because the return type accepts an object, but throws a fit about it
                        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                        // @ts-ignore
                        fields: () => {
                            const output: {[key: string]: {[key: string]: GraphQLNamedType | GraphQLList<any>}} = {};
                            output._record = {type: edgeRecordInfo};

                            relationship.keys?.forEach((relationshipKey) => {
                                const propertyName = stringToValidPropertyName(relationshipKey.property_name);

                                switch (relationshipKey.data_type) {
                                    // because we have no specification on our internal number type, we
                                    // must set this as a float for now
                                    case 'number': {
                                        output[propertyName] = {
                                            type: GraphQLFloat,
                                        };
                                        break;
                                    }

                                    case 'boolean': {
                                        output[propertyName] = {
                                            type: GraphQLBoolean,
                                        };
                                        break;
                                    }

                                    case 'string' || 'date' || 'file': {
                                        output[propertyName] = {
                                            type: GraphQLString,
                                        };
                                        break;
                                    }

                                    case 'list': {
                                        output[propertyName] = {
                                            type: new GraphQLList(GraphQLJSON),
                                        };
                                        break;
                                    }

                                    case 'enumeration': {
                                        const enumMap: {[key: string]: GraphQLEnumValueConfig} = {};

                                        if (relationshipKey.options) {
                                            relationshipKey.options.forEach((option) => {
                                                enumMap[option] = {
                                                    value: option,
                                                };
                                            });
                                        }

                                        output[propertyName] = {
                                            type: new GraphQLEnumType({
                                                name: stringToValidPropertyName(`${relationship.name}_${relationshipKey.name}_Enum_TypeA`),
                                                values: enumMap,
                                            }),
                                        };
                                        break;
                                    }

                                    default: {
                                        output[propertyName] = {
                                            type: GraphQLString,
                                        };
                                    }
                                }
                            });
                            return output;
                        },
                    }),
                ),
                resolve: this.resolverForRelationships(containerID, relationship, options),
            };
        });

        const metatypeObjects = new GraphQLObjectType({
            name: 'metatypes',
            fields: metatypeGraphQLObjects,
        });

        const relationshipObjects = new GraphQLObjectType({
            name: 'relationships',
            fields: relationshipGraphQLObjects,
        });

        // nodeType and edgeType for flexibility in filtering graph return
        const nodeInputType = new GraphQLInputObjectType({
            name: 'node_input',
            fields: {
                name: {type: GraphQLString},
                id: {type: GraphQLString},
                origin_name: {type: GraphQLString},
                origin_id: {type: GraphQLString},
                destination_name: {type: GraphQLString},
                destination_id: {type: GraphQLString},
            },
        });

        const edgeInputType = new GraphQLInputObjectType({
            name: 'edge_input',
            fields: {
                name: {type: GraphQLString},
                id: {type: GraphQLString},
            },
        });

        // the fields on which a user can filter the graph return
        const graphInput: {[key: string]: any} = {
            root_node: {type: new GraphQLNonNull(GraphQLString)}, // root node must be specified
            node_type: {type: nodeInputType},
            edge_type: {type: edgeInputType},
            depth: {type: new GraphQLNonNull(GraphQLString)}, // depth must be specified
        };

        const graphType = new GraphQLList(
            new GraphQLObjectType({
                name: 'graph_type',
                fields: {
                    // For more advanced querying these may become an Object type of their own
                    // to retrieve only specific properties from origin, edge and destination.
                    // For now json will do.
                    origin_properties: {type: GraphQLJSON},
                    edge_properties: {type: GraphQLJSON},
                    destination_properties: {type: GraphQLJSON},
                    // origin data
                    origin_id: {type: GraphQLString},
                    origin_metatype_name: {type: GraphQLString},
                    origin_metatype_id: {type: GraphQLString},
                    origin_data_source: {type: GraphQLString},
                    origin_metadata: {type: GraphQLJSON},
                    origin_created_by: {type: GraphQLString},
                    origin_created_at: {type: GraphQLString},
                    origin_modified_by: {type: GraphQLString},
                    origin_modified_at: {type: GraphQLString},
                    // edge data
                    edge_id: {type: GraphQLString},
                    relationship_name: {type: GraphQLString},
                    relationship_pair_id: {type: GraphQLString},
                    relationship_id: {type: GraphQLString},
                    edge_data_source: {type: GraphQLString},
                    edge_metadata: {type: GraphQLJSON},
                    edge_created_by: {type: GraphQLString},
                    edge_created_at: {type: GraphQLString},
                    edge_modified_by: {type: GraphQLString},
                    edge_modified_at: {type: GraphQLString},
                    // destination data
                    destination_id: {type: GraphQLString},
                    destination_metatype_name: {type: GraphQLString},
                    destination_metatype_id: {type: GraphQLString},
                    destination_data_source: {type: GraphQLString},
                    destination_metadata: {type: GraphQLJSON},
                    destination_created_by: {type: GraphQLString},
                    destination_created_at: {type: GraphQLString},
                    destination_modified_by: {type: GraphQLString},
                    destination_modified_at: {type: GraphQLString},
                    // graph metadata
                    depth: {type: GraphQLInt},
                    path: {type: GraphQLList(GraphQLString)},
                },
            }),
        );

        const fields: {[key: string]: any} = {};

        if (Object.keys(metatypeGraphQLObjects).length > 0) {
            fields.metatypes = {
                type: metatypeObjects,
                resolve: () => {
                    return metatypeGraphQLObjects;
                },
            };
        }

        if (Object.keys(relationshipGraphQLObjects).length > 0) {
            fields.relationships = {
                type: relationshipObjects,
                resolve: () => {
                    return relationshipGraphQLObjects;
                },
            };
        }

        fields.graph = {
            args: {...graphInput},
            type: (options.returnFile) ? fileInfo : graphType,
            resolve: this.resolverForGraph(containerID, options) as any,
        };

        return Promise.resolve(
            Result.Success(
                new GraphQLSchema({
                    query: new GraphQLObjectType({
                        name: 'Query',
                        fields,
                    }),
                }),
            ),
        );
    }