Merge pull request #40 from micimize/introspective-cycles

Make intropsection cycle-safe
This commit is contained in:
Tobe Osakwe 2020-02-03 15:23:26 -05:00 committed by GitHub
commit 4989183415
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 71 deletions

View file

@ -33,8 +33,8 @@ class GraphQL {
} }
if (introspect) { if (introspect) {
var allTypes = <GraphQLType>[]; var allTypes = fetchAllTypes(schema, [...this.customTypes]);
allTypes.addAll(this.customTypes);
_schema = reflectSchema(_schema, allTypes); _schema = reflectSchema(_schema, allTypes);
for (var type in allTypes.toSet()) { for (var type in allTypes.toSet()) {

View file

@ -7,21 +7,9 @@ import 'package:graphql_schema/graphql_schema.dart';
/// [allTypes] should contain all types, not directly defined in the schema, that you /// [allTypes] should contain all types, not directly defined in the schema, that you
/// would like to have introspection available for. /// would like to have introspection available for.
GraphQLSchema reflectSchema(GraphQLSchema schema, List<GraphQLType> allTypes) { GraphQLSchema reflectSchema(GraphQLSchema schema, List<GraphQLType> allTypes) {
for (var type in allTypes.toList()) {
var custom = _fetchAllTypesFromType(type);
for (var t in custom) {
if (!allTypes.contains(t)) {
allTypes.add(t);
}
}
}
var objectTypes = fetchAllTypes(schema, allTypes);
var typeType = _reflectSchemaTypes(); var typeType = _reflectSchemaTypes();
var directiveType = _reflectDirectiveType(); var directiveType = _reflectDirectiveType();
allTypes.addAll(objectTypes);
Set<GraphQLType> allTypeSet; Set<GraphQLType> allTypeSet;
var schemaType = objectType('__Schema', fields: [ var schemaType = objectType('__Schema', fields: [
@ -61,11 +49,11 @@ GraphQLSchema reflectSchema(GraphQLSchema schema, List<GraphQLType> allTypes) {
graphQLInt, graphQLInt,
directiveType, directiveType,
typeType, typeType,
directiveType,
schemaType, schemaType,
_typeKindType, _typeKindType,
_directiveLocationType, _directiveLocationType,
_reflectFields(), _reflectFields(),
_reflectDirectiveType(),
_reflectInputValueType(), _reflectInputValueType(),
_reflectEnumValueType(), _reflectEnumValueType(),
]); ]);
@ -108,9 +96,9 @@ GraphQLObjectType _reflectSchemaTypes() {
'ofType', 'ofType',
_reflectSchemaTypes(), _reflectSchemaTypes(),
resolve: (type, _) { resolve: (type, _) {
if (type is GraphQLListType) { if (type is GraphQLListType)
return type.ofType; return type.ofType;
} else if (type is GraphQLNonNullableType) return type.ofType; else if (type is GraphQLNonNullableType) return type.ofType;
return null; return null;
}, },
), ),
@ -236,7 +224,7 @@ GraphQLObjectType _createTypeType() {
'fields', 'fields',
listOf(fieldType), listOf(fieldType),
inputs: [ inputs: [
GraphQLFieldInput( new GraphQLFieldInput(
'includeDeprecated', 'includeDeprecated',
graphQLBoolean, graphQLBoolean,
defaultValue: false, defaultValue: false,
@ -253,7 +241,7 @@ GraphQLObjectType _createTypeType() {
'enumValues', 'enumValues',
listOf(enumValueType.nonNullable()), listOf(enumValueType.nonNullable()),
inputs: [ inputs: [
GraphQLFieldInput( new GraphQLFieldInput(
'includeDeprecated', 'includeDeprecated',
graphQLBoolean, graphQLBoolean,
defaultValue: false, defaultValue: false,
@ -434,71 +422,107 @@ GraphQLObjectType _reflectEnumValueType() {
} }
List<GraphQLType> fetchAllTypes( List<GraphQLType> fetchAllTypes(
GraphQLSchema schema, List<GraphQLType> allTypes) { GraphQLSchema schema, List<GraphQLType> specifiedTypes) {
var types = <GraphQLType>[]; var data = Set<GraphQLType>()
..add(schema.queryType)
types.addAll(_fetchAllTypesFromObject(schema.queryType)); ..addAll(specifiedTypes);
if (schema.mutationType != null) { if (schema.mutationType != null) {
types.addAll(_fetchAllTypesFromObject(schema.mutationType)); data.add(schema.mutationType);
} }
if (schema.subscriptionType != null) { if (schema.subscriptionType != null) {
types.addAll(_fetchAllTypesFromObject(schema.subscriptionType)); data.add(schema.subscriptionType);
} }
return types; return CollectTypes(data).types.toList();
} }
List<GraphQLType> _fetchAllTypesFromObject(GraphQLObjectType objectType) { class CollectTypes {
var types = <GraphQLType>[objectType]; Set<GraphQLType> traversedTypes = {};
for (var field in objectType.fields) { Set<GraphQLType> get types => traversedTypes;
if (field.type is GraphQLObjectType) {
types.addAll(_fetchAllTypesFromObject(field.type as GraphQLObjectType)); CollectTypes(Iterable<GraphQLType> types) {
} else if (field.type is GraphQLInputObjectType) { types.forEach(_fetchAllTypesFromType);
for (var v in (field.type as GraphQLInputObjectType).inputFields) { }
types.addAll(_fetchAllTypesFromType(v.type));
CollectTypes.fromRootObject(GraphQLObjectType type) {
_fetchAllTypesFromObject(type);
}
void _fetchAllTypesFromObject(GraphQLObjectType objectType) {
if (traversedTypes.contains(objectType)) {
return null;
}
traversedTypes.add(objectType);
for (var field in objectType.fields) {
if (field.type is GraphQLObjectType) {
_fetchAllTypesFromObject(field.type as GraphQLObjectType);
} else if (field.type is GraphQLInputObjectType) {
for (var v in (field.type as GraphQLInputObjectType).inputFields) {
_fetchAllTypesFromType(v.type);
}
} else {
_fetchAllTypesFromType(field.type);
}
for (var input in field.inputs ?? <GraphQLFieldInput>[]) {
_fetchAllTypesFromType(input.type);
} }
} else {
types.addAll(_fetchAllTypesFromType(field.type));
} }
for (var input in field.inputs ?? <GraphQLFieldInput>[]) { for (var i in objectType.interfaces) {
types.addAll(_fetchAllTypesFromType(input.type)); _fetchAllTypesFromObject(i);
} }
} }
for (var i in objectType.interfaces) { void _fetchAllTypesFromType(GraphQLType type) {
types.addAll(_fetchAllTypesFromObject(i)); if (traversedTypes.contains(type)) {
} return null;
}
return types; /*
} * Unwrap generics
*/
Iterable<GraphQLType> _fetchAllTypesFromType(GraphQLType type) { if (type is GraphQLNonNullableType) {
var types = <GraphQLType>[]; return _fetchAllTypesFromType(type.ofType);
}
if (type is GraphQLNonNullableType) { if (type is GraphQLListType) {
types.addAll(_fetchAllTypesFromType(type.ofType)); return _fetchAllTypesFromType(type.ofType);
} else if (type is GraphQLListType) { }
types.addAll(_fetchAllTypesFromType(type.ofType));
} else if (type is GraphQLObjectType) { /*
types.addAll(_fetchAllTypesFromObject(type)); * Handle simple types
} else if (type is GraphQLEnumType) { */
types.add(type); if (type is GraphQLEnumType) {
} else if (type is GraphQLInputObjectType) { traversedTypes.add(type);
for (var v in type.inputFields) { return null;
types.addAll(_fetchAllTypesFromType(v.type)); }
} if (type is GraphQLUnionType) {
traversedTypes.add(type);
types.add(type); for (var t in type.possibleTypes) {
} else if (type is GraphQLUnionType) { _fetchAllTypesFromType(t);
types.add(type); }
return null;
for (var t in type.possibleTypes) { }
types.addAll(_fetchAllTypesFromType(t)); if (type is GraphQLInputObjectType) {
} traversedTypes.add(type);
} for (var v in type.inputFields) {
return types; _fetchAllTypesFromType(v.type);
}
return null;
}
/*
* defer to object type traverser
*/
if (type is GraphQLObjectType) {
return _fetchAllTypesFromObject(type);
}
return null;
}
} }

View file

@ -4,7 +4,7 @@ author: Tobe O <thosakwe@gmail.com>
description: Base package for implementing GraphQL servers. You might prefer `package:angel_graphql`, the fastest way to implement GraphQL backends in Dart. description: Base package for implementing GraphQL servers. You might prefer `package:angel_graphql`, the fastest way to implement GraphQL backends in Dart.
homepage: https://github.com/angel-dart/graphql homepage: https://github.com/angel-dart/graphql
environment: environment:
sdk: ">=1.8.0 <3.0.0" sdk: ">=2.0.0 <3.0.0"
dependencies: dependencies:
angel_serialize: ^2.0.0 angel_serialize: ^2.0.0
collection: ^1.0.0 collection: ^1.0.0