From 4d33b7015cb19c399980b8ced799f65adbfcdae2 Mon Sep 17 00:00:00 2001 From: micimize Date: Sun, 19 Jan 2020 10:01:03 -0600 Subject: [PATCH 1/3] make intropsection cycle-safe --- graphql_server/lib/graphql_server.dart | 4 +- graphql_server/lib/introspection.dart | 167 ++++++++++++++----------- graphql_server/pubspec.yaml | 2 +- 3 files changed, 95 insertions(+), 78 deletions(-) diff --git a/graphql_server/lib/graphql_server.dart b/graphql_server/lib/graphql_server.dart index 042694ca..6e97dd65 100644 --- a/graphql_server/lib/graphql_server.dart +++ b/graphql_server/lib/graphql_server.dart @@ -33,8 +33,8 @@ class GraphQL { } if (introspect) { - var allTypes = []; - allTypes.addAll(this.customTypes); + var allTypes = fetchAllTypes(schema, [...this.customTypes]); + _schema = reflectSchema(_schema, allTypes); for (var type in allTypes.toSet()) { diff --git a/graphql_server/lib/introspection.dart b/graphql_server/lib/introspection.dart index 8ccd68da..7ba25cfb 100644 --- a/graphql_server/lib/introspection.dart +++ b/graphql_server/lib/introspection.dart @@ -7,21 +7,9 @@ import 'package:graphql_schema/graphql_schema.dart'; /// [allTypes] should contain all types, not directly defined in the schema, that you /// would like to have introspection available for. GraphQLSchema reflectSchema(GraphQLSchema schema, List 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 directiveType = _reflectDirectiveType(); - allTypes.addAll(objectTypes); + Set allTypeSet; var schemaType = objectType('__Schema', fields: [ @@ -61,11 +49,11 @@ GraphQLSchema reflectSchema(GraphQLSchema schema, List allTypes) { graphQLInt, directiveType, typeType, + directiveType, schemaType, _typeKindType, _directiveLocationType, _reflectFields(), - _reflectDirectiveType(), _reflectInputValueType(), _reflectEnumValueType(), ]); @@ -108,9 +96,9 @@ GraphQLObjectType _reflectSchemaTypes() { 'ofType', _reflectSchemaTypes(), resolve: (type, _) { - if (type is GraphQLListType) { + if (type is GraphQLListType) return type.ofType; - } else if (type is GraphQLNonNullableType) return type.ofType; + else if (type is GraphQLNonNullableType) return type.ofType; return null; }, ), @@ -236,7 +224,7 @@ GraphQLObjectType _createTypeType() { 'fields', listOf(fieldType), inputs: [ - GraphQLFieldInput( + new GraphQLFieldInput( 'includeDeprecated', graphQLBoolean, defaultValue: false, @@ -253,7 +241,7 @@ GraphQLObjectType _createTypeType() { 'enumValues', listOf(enumValueType.nonNullable()), inputs: [ - GraphQLFieldInput( + new GraphQLFieldInput( 'includeDeprecated', graphQLBoolean, defaultValue: false, @@ -434,71 +422,100 @@ GraphQLObjectType _reflectEnumValueType() { } List fetchAllTypes( - GraphQLSchema schema, List allTypes) { - var types = []; - - types.addAll(_fetchAllTypesFromObject(schema.queryType)); - - if (schema.mutationType != null) { - types.addAll(_fetchAllTypesFromObject(schema.mutationType)); - } - - if (schema.subscriptionType != null) { - types.addAll(_fetchAllTypesFromObject(schema.subscriptionType)); - } - - return types; + GraphQLSchema schema, List specifiedTypes) { + return CollectTypes({ + schema.queryType, + if (schema.mutationType != null) schema.mutationType, + if (schema.subscriptionType != null) schema.subscriptionType, + ...specifiedTypes, + }).types.toList(); } -List _fetchAllTypesFromObject(GraphQLObjectType objectType) { - var types = [objectType]; +class CollectTypes { + Set traversedTypes = {}; - for (var field in objectType.fields) { - if (field.type is GraphQLObjectType) { - types.addAll(_fetchAllTypesFromObject(field.type as GraphQLObjectType)); - } else if (field.type is GraphQLInputObjectType) { - for (var v in (field.type as GraphQLInputObjectType).inputFields) { - types.addAll(_fetchAllTypesFromType(v.type)); + Set get types => traversedTypes; + + CollectTypes(Iterable 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) { + 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 ?? []) { + _fetchAllTypesFromType(input.type); } - } else { - types.addAll(_fetchAllTypesFromType(field.type)); } - for (var input in field.inputs ?? []) { - types.addAll(_fetchAllTypesFromType(input.type)); + for (var i in objectType.interfaces) { + _fetchAllTypesFromObject(i); } } - for (var i in objectType.interfaces) { - types.addAll(_fetchAllTypesFromObject(i)); - } + void _fetchAllTypesFromType(GraphQLType type) { + if (traversedTypes.contains(type)) { + return null; + } - return types; -} - -Iterable _fetchAllTypesFromType(GraphQLType type) { - var types = []; - - if (type is GraphQLNonNullableType) { - types.addAll(_fetchAllTypesFromType(type.ofType)); - } else if (type is GraphQLListType) { - types.addAll(_fetchAllTypesFromType(type.ofType)); - } else if (type is GraphQLObjectType) { - 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) { - types.add(type); - - for (var t in type.possibleTypes) { - types.addAll(_fetchAllTypesFromType(t)); - } - } - return types; + /* + * Unwrap generics + */ + if (type is GraphQLNonNullableType) { + return _fetchAllTypesFromType(type.ofType); + } + if (type is GraphQLListType) { + return _fetchAllTypesFromType(type.ofType); + } + + /* + * Handle simple types + */ + if (type is GraphQLEnumType) { + traversedTypes.add(type); + return null; + } + if (type is GraphQLUnionType) { + traversedTypes.add(type); + for (var t in type.possibleTypes) { + _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; + } } diff --git a/graphql_server/pubspec.yaml b/graphql_server/pubspec.yaml index fd49048b..49acbabe 100644 --- a/graphql_server/pubspec.yaml +++ b/graphql_server/pubspec.yaml @@ -4,7 +4,7 @@ author: Tobe O 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 environment: - sdk: ">=1.8.0 <3.0.0" + sdk: ">=2.3.0 <3.0.0" dependencies: angel_serialize: ^2.0.0 collection: ^1.0.0 From 2038fddd6dff9e70c391ae0709927d27d4ca2a9e Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 3 Feb 2020 15:20:45 -0500 Subject: [PATCH 2/3] use old set syntax --- graphql_server/lib/introspection.dart | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/graphql_server/lib/introspection.dart b/graphql_server/lib/introspection.dart index 7ba25cfb..935f7fed 100644 --- a/graphql_server/lib/introspection.dart +++ b/graphql_server/lib/introspection.dart @@ -423,12 +423,19 @@ GraphQLObjectType _reflectEnumValueType() { List fetchAllTypes( GraphQLSchema schema, List specifiedTypes) { - return CollectTypes({ - schema.queryType, - if (schema.mutationType != null) schema.mutationType, - if (schema.subscriptionType != null) schema.subscriptionType, - ...specifiedTypes, - }).types.toList(); + var data = Set() + ..add(schema.queryType) + ..addAll(specifiedTypes); + + if (schema.mutationType != null) { + data.add(schema.mutationType); + } + + if (schema.subscriptionType != null) { + data.add(schema.subscriptionType); + } + + return CollectTypes(data).types.toList(); } class CollectTypes { From aebb97032a358ad41aed76a87ca80288ac09a2c1 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 3 Feb 2020 15:21:09 -0500 Subject: [PATCH 3/3] adjust sdk constraint --- graphql_server/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql_server/pubspec.yaml b/graphql_server/pubspec.yaml index 49acbabe..f05183e8 100644 --- a/graphql_server/pubspec.yaml +++ b/graphql_server/pubspec.yaml @@ -4,7 +4,7 @@ author: Tobe O 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 environment: - sdk: ">=2.3.0 <3.0.0" + sdk: ">=2.0.0 <3.0.0" dependencies: angel_serialize: ^2.0.0 collection: ^1.0.0