Merge pull request #40 from micimize/introspective-cycles
Make intropsection cycle-safe
This commit is contained in:
commit
4989183415
3 changed files with 95 additions and 71 deletions
|
@ -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()) {
|
||||||
|
|
|
@ -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 = {};
|
||||||
|
|
||||||
|
Set<GraphQLType> get types => traversedTypes;
|
||||||
|
|
||||||
|
CollectTypes(Iterable<GraphQLType> types) {
|
||||||
|
types.forEach(_fetchAllTypesFromType);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
for (var field in objectType.fields) {
|
||||||
if (field.type is GraphQLObjectType) {
|
if (field.type is GraphQLObjectType) {
|
||||||
types.addAll(_fetchAllTypesFromObject(field.type as GraphQLObjectType));
|
_fetchAllTypesFromObject(field.type as GraphQLObjectType);
|
||||||
} else if (field.type is GraphQLInputObjectType) {
|
} else if (field.type is GraphQLInputObjectType) {
|
||||||
for (var v in (field.type as GraphQLInputObjectType).inputFields) {
|
for (var v in (field.type as GraphQLInputObjectType).inputFields) {
|
||||||
types.addAll(_fetchAllTypesFromType(v.type));
|
_fetchAllTypesFromType(v.type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
types.addAll(_fetchAllTypesFromType(field.type));
|
_fetchAllTypesFromType(field.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var input in field.inputs ?? <GraphQLFieldInput>[]) {
|
for (var input in field.inputs ?? <GraphQLFieldInput>[]) {
|
||||||
types.addAll(_fetchAllTypesFromType(input.type));
|
_fetchAllTypesFromType(input.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i in objectType.interfaces) {
|
for (var i in objectType.interfaces) {
|
||||||
types.addAll(_fetchAllTypesFromObject(i));
|
_fetchAllTypesFromObject(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return types;
|
void _fetchAllTypesFromType(GraphQLType type) {
|
||||||
|
if (traversedTypes.contains(type)) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<GraphQLType> _fetchAllTypesFromType(GraphQLType type) {
|
/*
|
||||||
var types = <GraphQLType>[];
|
* Unwrap generics
|
||||||
|
*/
|
||||||
if (type is GraphQLNonNullableType) {
|
if (type is GraphQLNonNullableType) {
|
||||||
types.addAll(_fetchAllTypesFromType(type.ofType));
|
return _fetchAllTypesFromType(type.ofType);
|
||||||
} else if (type is GraphQLListType) {
|
}
|
||||||
types.addAll(_fetchAllTypesFromType(type.ofType));
|
if (type is GraphQLListType) {
|
||||||
} else if (type is GraphQLObjectType) {
|
return _fetchAllTypesFromType(type.ofType);
|
||||||
types.addAll(_fetchAllTypesFromObject(type));
|
|
||||||
} else if (type is GraphQLEnumType) {
|
|
||||||
types.add(type);
|
|
||||||
} else if (type is GraphQLInputObjectType) {
|
|
||||||
for (var v in type.inputFields) {
|
|
||||||
types.addAll(_fetchAllTypesFromType(v.type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
types.add(type);
|
/*
|
||||||
} else if (type is GraphQLUnionType) {
|
* Handle simple types
|
||||||
types.add(type);
|
*/
|
||||||
|
if (type is GraphQLEnumType) {
|
||||||
|
traversedTypes.add(type);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (type is GraphQLUnionType) {
|
||||||
|
traversedTypes.add(type);
|
||||||
for (var t in type.possibleTypes) {
|
for (var t in type.possibleTypes) {
|
||||||
types.addAll(_fetchAllTypesFromType(t));
|
_fetchAllTypesFromType(t);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (type is GraphQLInputObjectType) {
|
||||||
|
traversedTypes.add(type);
|
||||||
|
for (var v in type.inputFields) {
|
||||||
|
_fetchAllTypesFromType(v.type);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* defer to object type traverser
|
||||||
|
*/
|
||||||
|
if (type is GraphQLObjectType) {
|
||||||
|
return _fetchAllTypesFromObject(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue