Skip to content

Commit

Permalink
Handle object and list filters on nested GQL selections and update co…
Browse files Browse the repository at this point in the history
…degen (#532)

* Handle object type for where clause on nested GQL selections

* Handle list type for where clause on nested GQL selections

* Generate GQL schema types with arguments on plural fields
  • Loading branch information
prathamesh0 authored Aug 5, 2024
1 parent 05fdf85 commit d0f8875
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 35 deletions.
45 changes: 39 additions & 6 deletions packages/codegen/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ export class Schema {
}

_addSubgraphSchemaQueries (subgraphTypeDefs: ReadonlyArray<DefinitionNode>): void {
const subgraphTypeArgsMap = new Map<string, { [key: string]: any }>();

for (const subgraphTypeDef of subgraphTypeDefs) {
// Filtering out enums.
if (subgraphTypeDef.kind !== 'ObjectTypeDefinition') {
Expand Down Expand Up @@ -303,21 +305,30 @@ export class Schema {
let pluralQueryName = pluralize(queryName);
pluralQueryName = (pluralQueryName === queryName) ? `${pluralQueryName}s` : pluralQueryName;

const queryArgs = {
where: `${subgraphType}_filter`,
orderBy: subgraphTypeOrderByEnum,
orderDirection: ORDER_DIRECTION,
first: { type: GraphQLInt, defaultValue: 100 },
skip: { type: GraphQLInt, defaultValue: 0 }
};

queryObject[pluralQueryName] = {
// Get type composer object for return type from the schema composer.
type: this._composer.getAnyTC(subgraphType).NonNull.List.NonNull,
args: {
block: BLOCK_HEIGHT,
where: `${subgraphType}_filter`,
orderBy: subgraphTypeOrderByEnum,
orderDirection: ORDER_DIRECTION,
first: { type: GraphQLInt, defaultValue: 100 },
skip: { type: GraphQLInt, defaultValue: 0 }
...queryArgs
}
};

this._composer.Query.addFields(queryObject);

// Save the args for this type in a map (type -> args) for further usage.
subgraphTypeArgsMap.set(subgraphType, queryArgs);
}

// Add args on plural fields for subgraph types.
this._addSubgraphPluralFieldArgs(subgraphTypeDefs, subgraphTypeArgsMap);
}

_getDetailsForSubgraphField (fieldType: ComposeOutputType<any>): {
Expand Down Expand Up @@ -381,6 +392,28 @@ export class Schema {
});
}

_addSubgraphPluralFieldArgs (subgraphTypeDefs: ReadonlyArray<DefinitionNode>, subgraphTypeArgsMap: Map<string, { [key: string]: any }>): void {
for (const subgraphTypeDef of subgraphTypeDefs) {
// Filtering out enums.
if (subgraphTypeDef.kind !== 'ObjectTypeDefinition') {
continue;
}

const subgraphType = subgraphTypeDef.name.value;
const subgraphTypeComposer = this._composer.getOTC(subgraphType);

// Process each field on the type.
Object.entries(subgraphTypeComposer.getFields()).forEach(([fieldName, field]) => {
const { isArray, entityType } = this._getDetailsForSubgraphField(field.type);

// Set args if it's a plural field of some entity type.
if (entityType && isArray) {
subgraphTypeComposer.setFieldArgs(fieldName, subgraphTypeArgsMap.get(entityType) || {});
}
});
}
}

/**
* Adds basic types to the schema and typemapping.
*/
Expand Down
58 changes: 29 additions & 29 deletions packages/util/src/graph/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from 'typeorm';
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer';
import { ArgumentNode, FieldNode, GraphQLResolveInfo, SelectionNode, IntValueNode, EnumValueNode, ObjectValueNode, ObjectFieldNode } from 'graphql';
import { ArgumentNode, FieldNode, GraphQLResolveInfo, SelectionNode, IntValueNode, EnumValueNode, ObjectValueNode, ObjectFieldNode, ValueNode } from 'graphql';
import _ from 'lodash';
import debug from 'debug';

Expand Down Expand Up @@ -281,7 +281,7 @@ export class GraphDatabase {
childSelections = childSelections.filter(selection => !(selection.kind === 'Field' && selection.name.value === '__typename'));

// Parse selection's arguments
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getSelectionFieldArguments(selection, queryInfo);
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getGQLSelectionFieldArguments(selection, queryInfo);

if (isDerived) {
const where: Where = {
Expand Down Expand Up @@ -813,7 +813,7 @@ export class GraphDatabase {
childSelections = childSelections.filter(selection => !(selection.kind === 'Field' && selection.name.value === '__typename'));

// Parse selection's arguments
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getSelectionFieldArguments(selection, queryInfo);
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getGQLSelectionFieldArguments(selection, queryInfo);

if (isDerived) {
const where: Where = {
Expand Down Expand Up @@ -1456,14 +1456,14 @@ export class GraphDatabase {
}, []);
}

_getSelectionFieldArguments (fieldNode: FieldNode, queryInfo: GraphQLResolveInfo): { where: Where, queryOptions: QueryOptions } {
_getGQLSelectionFieldArguments (fieldNode: FieldNode, queryInfo: GraphQLResolveInfo): { where: Where, queryOptions: QueryOptions } {
let where: Where = {};
const queryOptions: QueryOptions = {};

fieldNode.arguments?.forEach((arg: ArgumentNode) => {
switch (arg.name.value) {
case 'where':
where = this.buildFilter(this._buildWhereFromArgumentNode(arg, queryInfo));
where = this.buildFilter(this._buildWhereFromGQLArgValue((arg.value as ObjectValueNode), queryInfo));
break;

case 'first': {
Expand Down Expand Up @@ -1500,33 +1500,33 @@ export class GraphDatabase {
return { where, queryOptions };
}

_buildWhereFromArgumentNode (arg: ArgumentNode, queryInfo: GraphQLResolveInfo): { [key: string]: any } {
// TODO: Handle all types of filters on nested fields

return (arg.value as ObjectValueNode).fields.reduce((acc: { [key: string]: any }, fieldNode: ObjectFieldNode) => {
switch (fieldNode.value.kind) {
case 'BooleanValue' :
case 'EnumValue' :
case 'FloatValue' :
case 'IntValue' :
case 'StringValue' :
acc[fieldNode.name.value] = fieldNode.value.value;
break;
_buildWhereFromGQLArgValue (whereArgValue: ObjectValueNode, queryInfo: GraphQLResolveInfo): { [key: string]: any } {
return whereArgValue.fields.reduce((acc: { [key: string]: any }, fieldNode: ObjectFieldNode) => {
acc[fieldNode.name.value] = this._parseGQLFieldValue(fieldNode.value, queryInfo);
return acc;
}, {});
}

case 'NullValue':
acc[fieldNode.name.value] = null;
break;
_parseGQLFieldValue (value: ValueNode, queryInfo: GraphQLResolveInfo): any {
switch (value.kind) {
case 'BooleanValue':
case 'EnumValue':
case 'FloatValue':
case 'IntValue':
case 'StringValue':
return value.value;

case 'Variable':
acc[fieldNode.name.value] = queryInfo.variableValues[fieldNode.value.name.value];
break;
case 'NullValue':
return null;

case 'ListValue':
case 'ObjectValue':
throw new Error(`Nested filter type ${fieldNode.value.kind} not supported`);
}
case 'Variable':
return queryInfo.variableValues[value.name.value];

return acc;
}, {});
case 'ListValue':
return value.values.map((valueNode) => this._parseGQLFieldValue(valueNode, queryInfo));

case 'ObjectValue':
return this._buildWhereFromGQLArgValue(value, queryInfo);
}
}
}

0 comments on commit d0f8875

Please sign in to comment.