mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-06 19:38:05 +00:00
Handle object and list filters on nested GQL selections and update codegen (#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
This commit is contained in:
parent
05fdf85af8
commit
d0f88756c3
@ -189,6 +189,8 @@ export class Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_addSubgraphSchemaQueries (subgraphTypeDefs: ReadonlyArray<DefinitionNode>): void {
|
_addSubgraphSchemaQueries (subgraphTypeDefs: ReadonlyArray<DefinitionNode>): void {
|
||||||
|
const subgraphTypeArgsMap = new Map<string, { [key: string]: any }>();
|
||||||
|
|
||||||
for (const subgraphTypeDef of subgraphTypeDefs) {
|
for (const subgraphTypeDef of subgraphTypeDefs) {
|
||||||
// Filtering out enums.
|
// Filtering out enums.
|
||||||
if (subgraphTypeDef.kind !== 'ObjectTypeDefinition') {
|
if (subgraphTypeDef.kind !== 'ObjectTypeDefinition') {
|
||||||
@ -303,21 +305,30 @@ export class Schema {
|
|||||||
let pluralQueryName = pluralize(queryName);
|
let pluralQueryName = pluralize(queryName);
|
||||||
pluralQueryName = (pluralQueryName === queryName) ? `${pluralQueryName}s` : pluralQueryName;
|
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] = {
|
queryObject[pluralQueryName] = {
|
||||||
// Get type composer object for return type from the schema composer.
|
// Get type composer object for return type from the schema composer.
|
||||||
type: this._composer.getAnyTC(subgraphType).NonNull.List.NonNull,
|
type: this._composer.getAnyTC(subgraphType).NonNull.List.NonNull,
|
||||||
args: {
|
args: {
|
||||||
block: BLOCK_HEIGHT,
|
block: BLOCK_HEIGHT,
|
||||||
where: `${subgraphType}_filter`,
|
...queryArgs
|
||||||
orderBy: subgraphTypeOrderByEnum,
|
|
||||||
orderDirection: ORDER_DIRECTION,
|
|
||||||
first: { type: GraphQLInt, defaultValue: 100 },
|
|
||||||
skip: { type: GraphQLInt, defaultValue: 0 }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._composer.Query.addFields(queryObject);
|
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>): {
|
_getDetailsForSubgraphField (fieldType: ComposeOutputType<any>): {
|
||||||
@ -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.
|
* Adds basic types to the schema and typemapping.
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
|
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
|
||||||
import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer';
|
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 _ from 'lodash';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ export class GraphDatabase {
|
|||||||
childSelections = childSelections.filter(selection => !(selection.kind === 'Field' && selection.name.value === '__typename'));
|
childSelections = childSelections.filter(selection => !(selection.kind === 'Field' && selection.name.value === '__typename'));
|
||||||
|
|
||||||
// Parse selection's arguments
|
// Parse selection's arguments
|
||||||
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getSelectionFieldArguments(selection, queryInfo);
|
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getGQLSelectionFieldArguments(selection, queryInfo);
|
||||||
|
|
||||||
if (isDerived) {
|
if (isDerived) {
|
||||||
const where: Where = {
|
const where: Where = {
|
||||||
@ -813,7 +813,7 @@ export class GraphDatabase {
|
|||||||
childSelections = childSelections.filter(selection => !(selection.kind === 'Field' && selection.name.value === '__typename'));
|
childSelections = childSelections.filter(selection => !(selection.kind === 'Field' && selection.name.value === '__typename'));
|
||||||
|
|
||||||
// Parse selection's arguments
|
// Parse selection's arguments
|
||||||
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getSelectionFieldArguments(selection, queryInfo);
|
let { where: relationWhere, queryOptions: relationQueryOptions } = this._getGQLSelectionFieldArguments(selection, queryInfo);
|
||||||
|
|
||||||
if (isDerived) {
|
if (isDerived) {
|
||||||
const where: Where = {
|
const where: Where = {
|
||||||
@ -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 = {};
|
let where: Where = {};
|
||||||
const queryOptions: QueryOptions = {};
|
const queryOptions: QueryOptions = {};
|
||||||
|
|
||||||
fieldNode.arguments?.forEach((arg: ArgumentNode) => {
|
fieldNode.arguments?.forEach((arg: ArgumentNode) => {
|
||||||
switch (arg.name.value) {
|
switch (arg.name.value) {
|
||||||
case 'where':
|
case 'where':
|
||||||
where = this.buildFilter(this._buildWhereFromArgumentNode(arg, queryInfo));
|
where = this.buildFilter(this._buildWhereFromGQLArgValue((arg.value as ObjectValueNode), queryInfo));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'first': {
|
case 'first': {
|
||||||
@ -1500,33 +1500,33 @@ export class GraphDatabase {
|
|||||||
return { where, queryOptions };
|
return { where, queryOptions };
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildWhereFromArgumentNode (arg: ArgumentNode, queryInfo: GraphQLResolveInfo): { [key: string]: any } {
|
_buildWhereFromGQLArgValue (whereArgValue: ObjectValueNode, queryInfo: GraphQLResolveInfo): { [key: string]: any } {
|
||||||
// TODO: Handle all types of filters on nested fields
|
return whereArgValue.fields.reduce((acc: { [key: string]: any }, fieldNode: ObjectFieldNode) => {
|
||||||
|
acc[fieldNode.name.value] = this._parseGQLFieldValue(fieldNode.value, queryInfo);
|
||||||
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;
|
|
||||||
|
|
||||||
case 'NullValue':
|
|
||||||
acc[fieldNode.name.value] = null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Variable':
|
|
||||||
acc[fieldNode.name.value] = queryInfo.variableValues[fieldNode.value.name.value];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ListValue':
|
|
||||||
case 'ObjectValue':
|
|
||||||
throw new Error(`Nested filter type ${fieldNode.value.kind} not supported`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_parseGQLFieldValue (value: ValueNode, queryInfo: GraphQLResolveInfo): any {
|
||||||
|
switch (value.kind) {
|
||||||
|
case 'BooleanValue':
|
||||||
|
case 'EnumValue':
|
||||||
|
case 'FloatValue':
|
||||||
|
case 'IntValue':
|
||||||
|
case 'StringValue':
|
||||||
|
return value.value;
|
||||||
|
|
||||||
|
case 'NullValue':
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case 'Variable':
|
||||||
|
return queryInfo.variableValues[value.name.value];
|
||||||
|
|
||||||
|
case 'ListValue':
|
||||||
|
return value.values.map((valueNode) => this._parseGQLFieldValue(valueNode, queryInfo));
|
||||||
|
|
||||||
|
case 'ObjectValue':
|
||||||
|
return this._buildWhereFromGQLArgValue(value, queryInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user