graphql#GraphQLNonNull TypeScript Examples
The following examples show how to use
graphql#GraphQLNonNull.
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 |
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 graphql-ts-client with MIT License | 6 votes |
export function targetTypeOf(type: GraphQLType): GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType | undefined {
if (type instanceof GraphQLNonNull) {
return targetTypeOf(type.ofType);
}
if (type instanceof GraphQLList) {
return targetTypeOf(type.ofType);
}
if (type instanceof GraphQLObjectType || type instanceof GraphQLInterfaceType || type instanceof GraphQLUnionType) {
return type;
}
return undefined;
}
Example #3
Source File: types.ts From tql with MIT License | 6 votes |
printField = (field: GraphQLField<any, any, any>): string => {
const { args } = field;
const isList = listType(field.type);
const isNonNull = field.type instanceof GraphQLNonNull;
const type = outputType(field.type);
const printVariables = () => {
return args.length > 0
? `(variables: { ${args.map(printVariable).join(", ")} })`
: "";
};
if (type instanceof GraphQLScalarType) {
return (
`${args.length > 0 ? "" : "readonly"} ${field.name}${printVariables()}: ${
isList ? `ReadonlyArray<${toPrimitive(type)}>` : `${toPrimitive(type)}`
}` + (isNonNull ? "" : " | null")
);
} else if (type instanceof GraphQLEnumType) {
return (
`${args.length > 0 ? "" : "readonly"} ${field.name}${printVariables()}: ${
isList ? `ReadonlyArray<${type.name}>` : `${type.name}`
}` + (isNonNull ? "" : " | null")
);
} else if (
type instanceof GraphQLInterfaceType ||
type instanceof GraphQLUnionType ||
type instanceof GraphQLObjectType
) {
return (
`${args.length > 0 ? "" : "readonly"} ${field.name}${printVariables()}: ${
isList ? `ReadonlyArray<I${type.name}>` : `I${type.name}`
}` + (isNonNull ? "" : " | null")
);
} else {
throw new Error("Unable to print field.");
}
}
Example #4
Source File: schema.ts From squid with GNU General Public License v3.0 | 6 votes |
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 #5
Source File: withNullableType.ts From payload with MIT License | 6 votes |
withNullableType = (field: NonPresentationalField, type: GraphQLType, forceNullable = false): GraphQLType => {
const hasReadAccessControl = field.access && field.access.read;
const condition = field.admin && field.admin.condition;
if (!forceNullable && field.required && !field.localized && !condition && !hasReadAccessControl) {
return new GraphQLNonNull(type);
}
return type;
}
Example #6
Source File: Writer.ts From graphql-ts-client with MIT License | 6 votes |
protected gqlTypeRef(type: GraphQLType) {
if (type instanceof GraphQLNonNull) {
this.gqlTypeRef(type.ofType);
this.text("!");
} else if (type instanceof GraphQLList) {
this.text("[");
this.gqlTypeRef(type.ofType);
this.text("]");
} else if (type instanceof GraphQLUnionType) {
this.enter("BLANK");
for (const itemType of type.getTypes()) {
this.separator(" | ");
this.text(itemType.name);
}
this.leave();
} else {
this.text(type.name);
}
}
Example #7
Source File: helpers.ts From amplify-codegen with Apache License 2.0 | 6 votes |
propertyFromInputField(field: GraphQLInputField) {
return Object.assign(
{},
{
propertyName: camelCase(field.name),
typeName: this.typeNameFromGraphQLType(field.type),
isOptional: !(field.type instanceof GraphQLNonNull),
description: field.description || null,
name: field.name,
}
);
}
Example #8
Source File: schema.ts From apollo-server-vercel with MIT License | 6 votes |
mutationType = new GraphQLObjectType({
name: `MutationType`,
fields: {
testMutation: {
type: GraphQLString,
args: { echo: { type: GraphQLString } },
resolve(_, { echo }): string {
return `not really a mutation, but who cares: ${String(echo)}`;
}
},
testPerson: {
type: personType,
args: {
firstName: {
type: new GraphQLNonNull(GraphQLString)
},
lastName: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(_, args): typeof args {
return args;
}
},
testRootValue: {
type: GraphQLString,
resolve(rootValue): typeof rootValue {
return rootValue;
}
}
}
})
Example #9
Source File: getType.test.ts From amplify-codegen with Apache License 2.0 | 6 votes |
describe('getType', () => {
const testObj = new GraphQLObjectType({
name: 'Address',
fields: {
street: { type: GraphQLString },
number: { type: GraphQLInt },
requiredInt: { type: new GraphQLNonNull(GraphQLInt) },
listOfInt: { type: new GraphQLList(GraphQLInt) },
listOfNonNullInt: { type: new GraphQLNonNull(new GraphQLList(GraphQLInt)) },
},
});
it('should return string type for street', () => {
expect(getType(testObj.getFields().street.type)).toEqual(GraphQLString);
});
it('should return integer type for number', () => {
expect(getType(testObj.getFields().number.type)).toEqual(GraphQLInt);
});
it('should return integer type for a Non-Null integer', () => {
expect(getType(testObj.getFields().requiredInt.type)).toEqual(GraphQLInt);
});
it('should return integer type for list of integer type', () => {
expect(getType(testObj.getFields().listOfInt.type)).toEqual(GraphQLInt);
});
it('should return integer type for a list of non null integer type', () => {
expect(getType(testObj.getFields().listOfNonNullInt.type)).toEqual(GraphQLInt);
});
});
Example #10
Source File: buildPoliciesType.ts From payload with MIT License | 5 votes |
buildFields = (label, fieldsToBuild) => fieldsToBuild.reduce((builtFields, field) => {
if (!field.hidden) {
if (field.name) {
const fieldName = formatName(field.name);
const objectTypeFields: ObjectTypeFields = ['create', 'read', 'update', 'delete'].reduce((operations, operation) => {
const capitalizedOperation = operation.charAt(0).toUpperCase() + operation.slice(1);
return {
...operations,
[operation]: {
type: new GraphQLObjectType({
name: `${label}_${fieldName}_${capitalizedOperation}`,
fields: {
permission: {
type: new GraphQLNonNull(GraphQLBoolean),
},
},
}),
},
};
}, {});
if (field.fields) {
objectTypeFields.fields = {
type: new GraphQLObjectType({
name: `${label}_${fieldName}_Fields`,
fields: buildFields(`${label}_${fieldName}`, field.fields),
}),
};
}
return {
...builtFields,
[field.name]: {
type: new GraphQLObjectType({
name: `${label}_${fieldName}`,
fields: objectTypeFields,
}),
},
};
}
if (!field.name && field.fields) {
const subFields = buildFields(label, field.fields);
return {
...builtFields,
...subFields,
};
}
}
return builtFields;
}, {})
Example #11
Source File: renderTyping.ts From genql with MIT License | 5 votes |
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: schema.ts From squid with GNU General Public License v3.0 | 5 votes |
function asNonNull(f: GraphQLField<any, any>): GraphQLOutputType {
let type = f.type
if (type instanceof GraphQLNonNull) {
type = type.ofType
}
return type
}
Example #13
Source File: simple.ts From graphql-sse with MIT License | 5 votes |
schemaConfig: GraphQLSchemaConfig = {
query: new GraphQLObjectType({
name: 'Query',
fields: {
getValue: {
type: new GraphQLNonNull(GraphQLString),
resolve: () => 'value',
},
},
}),
subscription: new GraphQLObjectType({
name: 'Subscription',
fields: {
greetings: {
type: new GraphQLNonNull(GraphQLString),
subscribe: async function* () {
for (const hi of ['Hi', 'Bonjour', 'Hola', 'Ciao', 'Zdravo']) {
yield { greetings: hi };
}
},
},
ping: {
type: new GraphQLNonNull(GraphQLString),
args: {
key: {
type: GraphQLString,
},
},
subscribe: function (_src, args) {
const key = args.key ? args.key : 'global';
return {
[Symbol.asyncIterator]() {
return this;
},
async next() {
if ((pendingPongs[key] ?? 0) > 0) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
pendingPongs[key]!--;
return { value: { ping: 'pong' } };
}
if (
await new Promise((resolve) => (pongListeners[key] = resolve))
)
return { done: true };
return { value: { ping: 'pong' } };
},
async return() {
pongListeners[key]?.(true);
delete pongListeners[key];
return { done: true };
},
async throw() {
throw new Error('Ping no gusta');
},
};
},
},
},
}),
}
Example #14
Source File: GraphQLStateFetcherWriter.ts From graphql-ts-client with MIT License | 5 votes |
private writeFlatType() {
const t = this.text.bind(this);
t(`\nexport interface ${this.modelType.name}FlatType`);
const superTypes = this.ctx.inheritanceInfo.upcastTypeMap.get(this.modelType);
if (superTypes !== undefined) {
const arr = Array.from(superTypes).filter(it => this.ctx.triggerableTypes.has(it));
if (arr.length !== 0) {
t(' extends ');
this.scope({type: "BLANK"}, () => {
for (const superType of arr) {
this.separator(", ");
t(`${superType.name}FlatType`);
}
});
}
}
this.scope({type: "BLOCK", multiLines: true, prefix: " ", suffix: "\n"}, () => {
if (this.modelType instanceof GraphQLObjectType || this.modelType instanceof GraphQLInterfaceType) {
const fieldMap = this.modelType.getFields();
for (const fieldName of this.declaredFieldNames) {
const field = fieldMap[fieldName]!;
const category = this.fieldCategoryMap.get(fieldName);
const targetType = targetTypeOf(field.type);
if (category === "SCALAR") {
t("readonly ");
t(fieldName);
if (!(field.type instanceof GraphQLNonNull)) {
t("?");
}
t(": ");
this.typeRef(field.type);
t(";\n");
} else if (targetType !== undefined && category !== "SCALAR") {
let nodeType = this.ctx.connections.get(targetType)?.nodeType ?? targetType;
const idField = this.ctx.idFieldMap.get(nodeType);
if (idField === undefined) {
throw new Error(`${nodeType.name} does not has id field`);
}
t("readonly ");
t(fieldName);
if (!(field.type instanceof GraphQLNonNull)) {
t("?");
}
t(": ");
this.typeRef(field.type, (type, field) => {
if (type === nodeType) {
return field.name === idField.name;
}
return true;
});
t(";\n");
}
}
}
});
}
Example #15
Source File: auth_builder.ts From graphql-mesh with MIT License | 5 votes |
/**
* Gets the viewer Object, resolve function, and arguments
*/
function getViewerOT<TSource, TContext, TArgs>(
name: string,
protocolName: string,
securityType: string,
queryFields: GraphQLFieldConfigMap<any, any>,
data: PreprocessingData<TSource, TContext, TArgs>,
logger: Logger
): Viewer<TSource, TContext, TArgs> {
const scheme: ProcessedSecurityScheme = data.security[protocolName];
// Resolve function:
const resolve = (root: any, args: any, context: any) => {
const security = {};
const saneProtocolName = Oas3Tools.sanitize(protocolName, Oas3Tools.CaseStyle.camelCase);
security[Oas3Tools.storeSaneName(saneProtocolName, protocolName, data.saneMap, logger)] = args;
/**
* Viewers are always root, so we can instantiate _openAPIToGraphQL here without
* previously checking for its existence
*/
return {
_openAPIToGraphQL: {
security,
},
};
};
// Arguments:
/**
* Do not sort because they are already "sorted" in preprocessing.
* Otherwise, for basic auth, "password" will appear before "username"
*/
const args = {};
if (typeof scheme === 'object') {
for (const parameterName in scheme.parameters) {
args[parameterName] = { type: new GraphQLNonNull(GraphQLString) };
}
}
let typeDescription = `A viewer for security scheme '${protocolName}'`;
/**
* HTTP authentication uses different schemes. It is not sufficient to name
* only the security type
*/
let description =
securityType === 'http'
? `A viewer that wraps all operations authenticated via security scheme ` +
`'${protocolName}', which is of type 'http' '${scheme.def.scheme}'`
: `A viewer that wraps all operations authenticated via security scheme ` +
`'${protocolName}', which is of type '${securityType}'`;
if (data.oass.length !== 1) {
typeDescription += ` in OAS '${scheme.oas.info?.title}'`;
description = `, in OAS '${scheme.oas.info?.title}`;
}
return {
type: new GraphQLObjectType({
name: Oas3Tools.capitalize(name), // Should already be sanitized and in camelCase
description: typeDescription,
fields: () => queryFields,
}),
resolve,
args,
description,
};
}
Example #16
Source File: Writer.ts From graphql-ts-client with MIT License | 5 votes |
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: selectors.ts From tql with MIT License | 5 votes |
printVariable = (arg: GraphQLArgument): string => {
return `${arg.name}${
arg.type instanceof GraphQLNonNull ? "" : "?"
}: Variable<string> | ${printInputType(arg.type)}`;
}
Example #18
Source File: Generator.ts From graphql-ts-client with MIT License | 5 votes |
function connectionTypeTuple(
type: GraphQLObjectType | GraphQLInterfaceType
): [
GraphQLObjectType | GraphQLInterfaceType,
GraphQLObjectType | GraphQLInterfaceType,
GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType
] | undefined {
const edges = type.getFields()["edges"];
if (edges !== undefined) {
const listType =
edges.type instanceof GraphQLNonNull ?
edges.type.ofType :
edges.type;
if (listType instanceof GraphQLList) {
const edgeType =
listType.ofType instanceof GraphQLNonNull ?
listType.ofType.ofType :
listType.ofType;
if (edgeType instanceof GraphQLObjectType) {
const node = edgeType.getFields()["node"];
if (node !== undefined) {
if (!(edges.type instanceof GraphQLNonNull)) {
waring(
`The type "${type.name}" is connection, its field "edges" must be not-null list`
);
}
if (!(listType.ofType instanceof GraphQLNonNull)) {
waring(
`The type "${type.name}" is connection, element of its field "edges" must be not-null`
);
}
let nodeType: GraphQLType;
if (node.type instanceof GraphQLNonNull) {
nodeType = node.type.ofType;
} else {
waring(
`The type "${edgeType}" is edge, its field "node" must be non-null`
);
nodeType = node.type;
}
if (!(nodeType instanceof GraphQLObjectType) && !(nodeType instanceof GraphQLInterfaceType) && !(nodeType instanceof GraphQLUnionType)) {
throw new Error(
`The type "${edgeType}" is edge, its field "node" must be object, interface, union or their non-null wrappers`
);
}
const cursor = edgeType.getFields()["cursor"];
if (cursor === undefined) {
waring(
`The type "${edgeType}" is edge, it must defined a field named "cursor"`
);
} else {
const cursorType =
cursor.type instanceof GraphQLNonNull ?
cursor.type.ofType :
cursor.type;
if (cursorType !== GraphQLString) {
throw new Error(
`The type "${edgeType}" is edge, its field "cursor" must be string`
);
}
}
return [type, edgeType, nodeType];
}
}
}
}
return undefined;
}
Example #19
Source File: schema.ts From apollo-server-vercel with MIT License | 5 votes |
queryType = new GraphQLObjectType({
name: `QueryType`,
fields: {
testString: {
type: GraphQLString,
resolve(): string {
return `it works`;
}
},
testPerson: {
type: personType,
resolve(): { firstName: string; lastName: string } {
return { firstName: `Jane`, lastName: `Doe` };
}
},
testStringWithDelay: {
type: GraphQLString,
args: {
delay: { type: new GraphQLNonNull(GraphQLInt) }
},
async resolve(_, args): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => resolve(`it works`), args.delay);
});
}
},
testContext: {
type: GraphQLString,
resolve(_parent, _args, context): string {
if (context.otherField) {
return `unexpected`;
}
context.otherField = true;
return context.testField;
}
},
testRootValue: {
type: GraphQLString,
resolve(rootValue): typeof rootValue {
return rootValue;
}
},
testArgument: {
type: GraphQLString,
args: { echo: { type: GraphQLString } },
resolve(_, { echo }): string {
return `hello ${String(echo)}`;
}
},
testError: {
type: GraphQLString,
resolve(): void {
throw new Error(`Secret error message`);
}
}
}
})
Example #20
Source File: typeNameFromGraphQLType.ts From amplify-codegen with Apache License 2.0 | 5 votes |
describe('Swift code generation: Types', () => {
let helpers: Helpers;
beforeEach(() => {
helpers = new Helpers({});
});
describe('#typeNameFromGraphQLType()', () => {
it('should return String? for GraphQLString', () => {
expect(helpers.typeNameFromGraphQLType(GraphQLString)).toBe('String?');
});
it('should return String for GraphQLNonNull(GraphQLString)', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLNonNull(GraphQLString))).toBe('String');
});
it('should return [String?]? for GraphQLList(GraphQLString)', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLList(GraphQLString))).toBe('[String?]?');
});
it('should return [String?] for GraphQLNonNull(GraphQLList(GraphQLString))', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLString)))).toBe('[String?]');
});
it('should return [String]? for GraphQLList(GraphQLNonNull(GraphQLString))', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLString)))).toBe('[String]?');
});
it('should return [String] for GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString)))', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))))).toBe('[String]');
});
it('should return [[String?]?]? for GraphQLList(GraphQLList(GraphQLString))', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLList(new GraphQLList(GraphQLString)))).toBe('[[String?]?]?');
});
it('should return [[String?]]? for GraphQLList(GraphQLNonNull(GraphQLList(GraphQLString)))', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLList(new GraphQLNonNull(new GraphQLList(GraphQLString))))).toBe('[[String?]]?');
});
it('should return Int? for GraphQLInt', () => {
expect(helpers.typeNameFromGraphQLType(GraphQLInt)).toBe('Int?');
});
it('should return Double? for GraphQLFloat', () => {
expect(helpers.typeNameFromGraphQLType(GraphQLFloat)).toBe('Double?');
});
it('should return Bool? for GraphQLBoolean', () => {
expect(helpers.typeNameFromGraphQLType(GraphQLBoolean)).toBe('Bool?');
});
it('should return GraphQLID? for GraphQLID', () => {
expect(helpers.typeNameFromGraphQLType(GraphQLID)).toBe('GraphQLID?');
});
it('should return String? for a custom scalar type', () => {
expect(helpers.typeNameFromGraphQLType(new GraphQLScalarType({ name: 'CustomScalarType', serialize: String }))).toBe('String?');
});
it('should return a passed through custom scalar type with the passthroughCustomScalars option', () => {
helpers.options.passthroughCustomScalars = true;
helpers.options.customScalarsPrefix = '';
expect(helpers.typeNameFromGraphQLType(new GraphQLScalarType({ name: 'CustomScalarType', serialize: String }))).toBe(
'CustomScalarType?'
);
});
it('should return a passed through custom scalar type with a prefix with the customScalarsPrefix option', () => {
helpers.options.passthroughCustomScalars = true;
helpers.options.customScalarsPrefix = 'My';
expect(helpers.typeNameFromGraphQLType(new GraphQLScalarType({ name: 'CustomScalarType', serialize: String }))).toBe(
'MyCustomScalarType?'
);
});
});
});
Example #21
Source File: schema.ts From squid with GNU General Public License v3.0 | 4 votes |
function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType | GraphQLInterfaceType): void {
if (model[type.name]) return
let kind: 'entity' | 'object' | 'interface' = isEntityType(type)
? 'entity'
: type instanceof GraphQLInterfaceType ? 'interface' : 'object'
let properties: Record<string, Prop> = {}
let interfaces: string[] = []
let indexes: Index[] = type instanceof GraphQLObjectType ? checkEntityIndexes(type) : []
let description = type.description || undefined
switch(kind) {
case 'entity':
model[type.name] = {kind, properties, description, interfaces, indexes}
break
case 'object':
model[type.name] = {kind, properties, description, interfaces}
break
case 'interface':
model[type.name] = {kind, properties, description}
break
default:
throw unexpectedCase(kind)
}
let fields = type.getFields()
if (kind == 'entity') {
if (fields.id == null) {
properties.id = {
type: {kind: 'scalar', name: 'ID'},
nullable: false
}
} else {
let correctIdType = fields.id.type instanceof GraphQLNonNull
&& fields.id.type.ofType instanceof GraphQLScalarType
&& fields.id.type.ofType.name === 'ID'
if (!correctIdType) {
throw unsupportedFieldTypeError(type.name + '.id')
}
}
}
for (let key in fields) {
let f: GraphQLField<any, any> = fields[key]
handleFulltextDirective(model, type, f)
let propName = `${type.name}.${f.name}`
let fieldType = f.type
let nullable = true
let description = f.description || undefined
let derivedFrom = checkDerivedFrom(type, f)
let index = checkFieldIndex(type, f)
let unique = index?.unique || false
if (index) {
indexes.push(index)
}
if (fieldType instanceof GraphQLNonNull) {
nullable = false
fieldType = fieldType.ofType
}
let list = unwrapList(fieldType)
fieldType = list.item
if (fieldType instanceof GraphQLScalarType) {
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'scalar',
name: fieldType.name
}),
nullable,
description
}
} else if (fieldType instanceof GraphQLEnumType) {
addEnum(model, fieldType)
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'enum',
name: fieldType.name
}),
nullable,
description
}
} else if (fieldType instanceof GraphQLUnionType) {
addUnion(model, fieldType)
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'union',
name: fieldType.name
}),
nullable,
description
}
} else if (fieldType instanceof GraphQLObjectType) {
if (isEntityType(fieldType)) {
switch(list.nulls.length) {
case 0:
if (derivedFrom) {
if (!nullable) {
throw new SchemaError(`Property ${propName} must be nullable`)
}
properties[key] = {
type: {
kind: 'lookup',
entity: fieldType.name,
field: derivedFrom.field
},
nullable,
description
}
} else {
if (unique && nullable) {
throw new SchemaError(`Unique property ${propName} must be non-nullable`)
}
properties[key] = {
type: {
kind: 'fk',
foreignEntity: fieldType.name
},
nullable,
unique,
description
}
}
break
case 1:
if (derivedFrom == null) {
throw new SchemaError(`@derivedFrom directive is required on ${propName} declaration`)
}
properties[key] = {
type: {
kind: 'list-lookup',
entity: fieldType.name,
field: derivedFrom.field
},
nullable: false,
description
}
break
default:
throw unsupportedFieldTypeError(propName)
}
} else {
addEntityOrJsonObjectOrInterface(model, fieldType)
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'object',
name: fieldType.name
}),
nullable,
description
}
}
} else {
throw unsupportedFieldTypeError(propName)
}
}
if (kind != 'interface') {
type.getInterfaces().forEach(i => {
addEntityOrJsonObjectOrInterface(model, i)
interfaces.push(i.name)
})
}
}
Example #22
Source File: helpers.ts From amplify-codegen with Apache License 2.0 | 4 votes |
describe('Flow typeAnnotationFromGraphQLType', () => {
test('String', () => {
expect(typeAnnotationFromGraphQLType(GraphQLString)).toMatchObject(t.nullableTypeAnnotation(t.stringTypeAnnotation()));
});
test('Int', () => {
expect(typeAnnotationFromGraphQLType(GraphQLInt)).toMatchObject(t.nullableTypeAnnotation(t.numberTypeAnnotation()));
});
test('Float', () => {
expect(typeAnnotationFromGraphQLType(GraphQLFloat)).toMatchObject(t.nullableTypeAnnotation(t.numberTypeAnnotation()));
});
test('Boolean', () => {
expect(typeAnnotationFromGraphQLType(GraphQLBoolean)).toMatchObject(t.nullableTypeAnnotation(t.booleanTypeAnnotation()));
});
test('ID', () => {
expect(typeAnnotationFromGraphQLType(GraphQLID)).toMatchObject(t.nullableTypeAnnotation(t.stringTypeAnnotation()));
});
test('String!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLString))).toMatchObject(t.stringTypeAnnotation());
});
test('Int!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLInt))).toMatchObject(t.numberTypeAnnotation());
});
test('Float!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLFloat))).toMatchObject(t.numberTypeAnnotation());
});
test('Boolean!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLBoolean))).toMatchObject(t.booleanTypeAnnotation());
});
test('ID!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLID))).toMatchObject(t.stringTypeAnnotation());
});
// TODO: Test GenericTypeAnnotation
test('[String]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLString))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation())))
);
});
test('[Int]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLInt))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation())))
);
});
test('[Float]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLFloat))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation())))
);
});
test('[Boolean]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLBoolean))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.booleanTypeAnnotation())))
);
});
test('[ID]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLID))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation())))
);
});
test('[String]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLString)))).toMatchObject(
t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))
);
});
test('[Int]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLInt)))).toMatchObject(
t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation()))
);
});
test('[Float]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLFloat)))).toMatchObject(
t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation()))
);
});
test('[Boolean]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLBoolean)))).toMatchObject(
t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.booleanTypeAnnotation()))
);
});
test('[ID]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLID)))).toMatchObject(
t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))
);
});
test('[String!]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLString)))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.stringTypeAnnotation()))
);
});
test('[Int!]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLInt)))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.numberTypeAnnotation()))
);
});
test('[Float!]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLFloat)))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.numberTypeAnnotation()))
);
});
test('[Boolean!]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLBoolean)))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.booleanTypeAnnotation()))
);
});
test('[ID!]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLID)))).toMatchObject(
t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.stringTypeAnnotation()))
);
});
test('[String!]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))))).toMatchObject(
t.arrayTypeAnnotation(t.stringTypeAnnotation())
);
});
test('[Int!]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLInt))))).toMatchObject(
t.arrayTypeAnnotation(t.numberTypeAnnotation())
);
});
test('[Float!]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLFloat))))).toMatchObject(
t.arrayTypeAnnotation(t.numberTypeAnnotation())
);
});
test('[Boolean!]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLBoolean))))).toMatchObject(
t.arrayTypeAnnotation(t.booleanTypeAnnotation())
);
});
test('[ID!]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))))).toMatchObject(
t.arrayTypeAnnotation(t.stringTypeAnnotation())
);
});
test('[[String]]', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLList(GraphQLString)))).toMatchObject(
t.nullableTypeAnnotation(
t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))))
)
);
});
test('[[String]]!', () => {
expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLList(GraphQLString))))).toMatchObject(
t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))))
);
});
test('Custom Scalar', () => {
const OddType = new GraphQLScalarType({
name: 'Odd',
serialize(value) {
return value % 2 === 1 ? value : null;
},
});
expect(typeAnnotationFromGraphQLType(OddType)).toMatchObject(t.nullableTypeAnnotation(t.genericTypeAnnotation(t.identifier('Odd'))));
});
});
Example #23
Source File: schema_builder.ts From graphql-mesh with MIT License | 4 votes |
/**
* 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 #24
Source File: generateSchema.ts From davinci with MIT License | 4 votes |
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 #25
Source File: buildMutationInputType.ts From payload with MIT License | 4 votes |
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 #26
Source File: schema.ts From Deep-Lynx with MIT License | 4 votes |
// 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,
}),
}),
),
);
}
Example #27
Source File: schema.ts From peterportal-public-api with MIT License | 4 votes |
queryType: GraphQLObjectType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
// query course by ID
course: {
type: courseType,
// specify args to query by
args: {
id: { type: new GraphQLNonNull(GraphQLString), description: "Course Department concatenated with Course Number. Ex: COMPSCI161" }
},
// define function to get a course
resolve: (_, {id}) => {
return getCourse(id);
},
// documentation
description: "Search courses by their course id. Ex: COMPSCI161"
},
// get instructor by ucinetid
instructor: {
type: instructorType,
// specify args to query by (ucinetid)
args: {
ucinetid: { type: new GraphQLNonNull(GraphQLString), description: "ucinetid of a specific instructor. Ex: mikes"}
},
// define function to get a instructor
resolve: (_, {ucinetid}) => {
return getInstructor(ucinetid);
},
// documentation for instructor
description: "Search instructors by their ucinetid"
},
// return all courses
allCourses: {
type: new GraphQLList(courseType),
// get all courses from courses cache
resolve: () => {
return getAllCourses()
},
// documentation for all courses
description: "Return all courses. Takes no arguments"
},
// return all instructor
allInstructors: {
type: new GraphQLList(instructorType),
// get all instructors from cache
resolve: () => {
return getAllInstructors();
},
// documentation for all instructors
description: "Return all instructors. Takes no arguments"
},
schedule: {
type: new GraphQLList(courseOfferingType),
args: {
year: { type: new GraphQLNonNull(GraphQLFloat), description: "Year of the term. Required." },
quarter: { type: new GraphQLNonNull(GraphQLString), description: "Quarter of the term. ['Fall'|'Winter'|'Spring'|'Summer1'|'Summer2'|'Summer10wk']. Required." },
ge: { type: GraphQLString, description: "GE type. ['ANY'|'GE-1A'|'GE-1B'|'GE-2'|'GE-3'|'GE-4'|'GE-5A'|'GE-5B'|'GE-6'|'GE-7'|'GE-8']." },
department: { type: GraphQLString, description: "Department Code." },
course_number: { type: GraphQLString, description: "Course number or range. Ex: '32A' or '31-33'." },
division: { type: GraphQLString, description: "Division level of a course. ['ALL'|'LowerDiv'|'UpperDiv'|'Graduate']. Default: 'ALL'." },
section_codes: { type: GraphQLString, description: "5-digit course code or range. Ex: '36531' or '36520-36536'." },
instructor: { type: GraphQLString, description: "Instructor last name or part of last name." },
course_title: { type: GraphQLString, description: "Title of a course." },
section_type: { type: GraphQLString, description: "Type of section. ['ALL'|'ACT'|'COL'|'DIS'|'FLD'|'LAB'|'LEC'|'QIZ'|'RES'|'SEM'|'STU'|'TAP'|'TUT']. Default: 'ALL'." },
units: { type: GraphQLString, description: "Unit count of a course. Ex: '4' or '1.3'. Use 'VAR' to look for variable unit classes." },
days: { type: GraphQLString, description: "Days that a course is offered. Any combination of ['Su'|'M'|'T'|'W'|'Th'|'F'|'Sa']. Ex: 'MWF'." },
start_time: { type: GraphQLString, description: "Start time of a couse in 12 hour format. Ex: '10:00AM' or '5:00PM'" },
end_time: { type: GraphQLString, description: "End time of a couse in 12 hour format. Ex: '10:00AM' or '5:00PM'" },
max_capacity: { type: GraphQLString, description: "Maximum enrollment capacity of a choice. Specify a " },
full_courses: { type: GraphQLString, description: "Status of a course's enrollment state. ['ANY'|'SkipFullWaitlist'|'FullOnly'|'OverEnrolled']. Default: 'ANY'." },
cancelled_courses: { type: GraphQLString, description: "Indicate whether to ['Exclude'|'Include'|'Only'] cancelled courses. Default: 'EXCLUDE'." },
building: { type: GraphQLString, description: "Building code found on https://www.reg.uci.edu/addl/campus/." },
room: { type: GraphQLString, description: "Room number in a building. Must also specify a building code." }
},
resolve: async (_, args) => {
validateScheduleArgs(args);
const query = scheduleArgsToQuery(args);
const results: CourseOffering[] = await getCourseSchedules(query);
return results;
},
description: "Return schedule from websoc."
},
// return week of 'date' argument or current week if 'date' is empty.
week: {
type: weekType,
//date argument
args: {
year: { type: GraphQLString, description: "Must be in ISO 8601 extended format `YYYY`. "},
month: { type: GraphQLString, description: "Must be in ISO 8601 extended format `MM`. "},
day: { type: GraphQLString, description: "Must be in ISO 8601 extended format `DD`. "}
},
//calls getWeek(), fetching from UCI's academic calendar
resolve: async (_, {year, month, day}) => {
try{
return await getWeek(year, month, day);
}
catch(e) {
throw new ValidationError("Invalid year, month or day. Must include all year, month and day or none.")
}
},
description: "Returns the week and quarter, given a specific date. No parameters given uses today's date. Must include all year, month and day or none. "
},
grades: {
type: gradeDistributionCollectionType,
args: {
year: { type: GraphQLString, description: "Must be <START_YEAR>-<END_YEAR>. Ex. 2020-2021. Multiple values in the arguments can be included by using ; as a separator."},
quarter: { type: GraphQLString, description: "Fall | Winter | Spring | Summer Multiple values in the arguments can be included by using ; as a separator."},
instructor: { type: GraphQLString, description: "Instructor, must following the format (<last_name>, <first_initial>.) Multiple values in the arguments can be included by using ; as a separator."},
department: { type: GraphQLString, description: "Department short-hand. Ex. COMPSCI. Multiple values in the arguments can be included by using ; as a separator."},
number: { type: GraphQLString, description: "Course number. Multiple values in the arguments can be included by using ; as a separator."},
code: { type: GraphQLString, description: "5-digit course code on WebSoC. Multiple values in the arguments can be included by using ; as a separator." },
division: { type: GraphQLString, description: "Filter by Course Level ('LowerDiv'|'UpperDiv')"},
excludePNP: { type: GraphQLBoolean, description: "Exclude P/NP Only courses" }
},
resolve: (_, args, __, info) => {
// Get the fields requested in the query
// This allows us to only fetch what the client wants from sql
const requestedFields : string[] = Object.keys(parseResolveInfo(info).fieldsByTypeName.GradeDistributionCollection)
// Construct a WHERE clause from the arguments
const where : WhereParams = parseGradesParamsToSQL(args);
// If requested, retrieve the grade distributions
let grade_distributions: GradeGQLData[];
let rawGrades : GradeRawData = undefined;
if (requestedFields.includes('grade_distributions')) {
rawGrades = fetchGrades(where)
// Format the results to GraphQL
grade_distributions = rawGrades.map(result => {
return {
grade_a_count: result.gradeACount,
grade_b_count: result.gradeBCount,
grade_c_count: result.gradeCCount,
grade_d_count: result.gradeDCount,
grade_f_count: result.gradeFCount,
grade_p_count: result.gradePCount,
grade_np_count: result.gradeNPCount,
grade_w_count: result.gradeWCount,
average_gpa: result.averageGPA ?? null,
course_offering: {
year: result.year,
quarter: result.quarter,
section: {
code: result.code,
number: result.section,
type: result.type,
},
instructors: [result.instructor],
course: {
id: result.department.replace(/\s/g, '')+result.number,
department: result.department,
number: result.number,
department_name: result.department_name,
title: result.title
}
}
}
})
}
// If requested, retrieve the aggregate
let aggregate : GradeDistAggregate = undefined;
if (requestedFields.includes('aggregate')) {
const aggregateResult = fetchAggregatedGrades(where)
// Format results to GraphQL
aggregate = {
sum_grade_a_count: aggregateResult['sum_grade_a_count'],
sum_grade_b_count: aggregateResult['sum_grade_b_count'],
sum_grade_c_count: aggregateResult['sum_grade_c_count'],
sum_grade_d_count: aggregateResult['sum_grade_d_count'],
sum_grade_f_count: aggregateResult['sum_grade_f_count'],
sum_grade_p_count: aggregateResult['sum_grade_p_count'],
sum_grade_np_count: aggregateResult['sum_grade_np_count'],
sum_grade_w_count: aggregateResult['sum_grade_w_count'],
average_gpa: aggregateResult['average_gpa'],
count: aggregateResult['count']
}
}
// If requested, retrieve the instructors
let instructors : string[] = undefined;
if (requestedFields.includes('instructors')) {
if (rawGrades) {
// If the grade results exist, we can get the instructors from there
instructors = [...new Set(rawGrades.map(result => result.instructor))]
} else {
// Else query sql for the instructors
instructors = fetchInstructors(where)
}
}
// Return results
return {
aggregate,
grade_distributions,
instructors
}
},
description: "Search for grade distributions. Multiple values in the arguments can be included by using ; as a separator. "
}
})
})
Example #28
Source File: index.test.ts From hono with MIT License | 4 votes |
describe('Error handling functionality', () => {
const app = new Hono()
app.use(
'/graphql',
graphqlServer({
schema: TestSchema,
})
)
it('Handles query errors from non-null top field errors', async () => {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
test: {
type: new GraphQLNonNull(GraphQLString),
resolve() {
throw new Error('Throws!')
},
},
},
}),
})
const app = new Hono()
app.use('/graphql', graphqlServer({ schema }))
const res = await app.request(
urlString({
query: '{ test }',
})
)
expect(res.status).toBe(500)
})
it('Handles syntax errors caught by GraphQL', async () => {
const res = await app.request(
urlString({
query: 'syntax_error',
}),
{
method: 'GET',
}
)
expect(res.status).toBe(400)
})
it('Handles errors caused by a lack of query', async () => {
const res = await app.request(urlString(), {
method: 'GET',
})
expect(res.status).toBe(400)
})
it('Handles invalid JSON bodies', async () => {
const res = await app.request(urlString(), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify([]),
})
expect(res.status).toBe(400)
})
it('Handles incomplete JSON bodies', async () => {
const res = await app.request(urlString(), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: '{"query":',
})
expect(res.status).toBe(400)
})
it('Handles plain POST text', async () => {
const res = await app.request(
urlString({
variables: JSON.stringify({ who: 'Dolly' }),
}),
{
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: 'query helloWho($who: String){ test(who: $who) }',
}
)
expect(res.status).toBe(400)
})
it('Handles poorly formed variables', async () => {
const res = await app.request(
urlString({
variables: 'who:You',
query: 'query helloWho($who: String){ test(who: $who) }',
}),
{
method: 'GET',
}
)
expect(res.status).toBe(400)
})
it('Handles invalid variables', async () => {
const res = await app.request(urlString(), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: 'query helloWho($who: String){ test(who: $who) }',
variables: { who: ['John', 'Jane'] },
}),
})
expect(res.status).toBe(500)
})
it('Handles unsupported HTTP methods', async () => {
const res = await app.request(urlString({ query: '{test}' }), {
method: 'PUT',
})
expect(res.status).toBe(405)
expect(res.headers.get('allow')).toBe('GET, POST')
expect(await res.json()).toEqual({
errors: [{ message: 'GraphQL only supports GET and POST requests.' }],
})
})
})
Example #29
Source File: index.ts From graphql-mesh with MIT License | 4 votes |
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,
};
}