diff --git a/CHANGELOG.md b/CHANGELOG.md index 89039350..77fbe213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # CHANGELOG +## 5.0.0 +**MAJOR BREAKING CHANGE** +In this version we moved from `json` to `graphql` (SDL) schema parsing. +This allowed us to get rid off ±1200 lines of code which makes the +project support much easier. The test files with schema definitions +became more clear and human readable. + +If you already have your schema in SDL format, just point to it in `build.yaml`. +If not, use this [snippet][introspection-to-sdl-snippet] +(from [this Apollo article][apollo-3-ways-schema]) or online helpers like +[this one][introspection-to-sdl-online] to convert from one to another. + ## 4.0.2 - Only add unknownEnumValue on non-list enums - Consider all classes to include reference to meta package @@ -61,7 +73,7 @@ avoiding breaking/crashing the client. - Allow to dispose `ArtemisClient` underlining http client when possible ## 3.0.0 -- BREAKING: Marks non nullable input field as `@required` [#68](https://github.com/comigor/artemis/pull/68) +- BREAKING: Marks non nullable input field as `@required` [#68][pr-68] ## 2.2.2 - Make lists as input objects work again @@ -73,7 +85,7 @@ avoiding breaking/crashing the client. - Add "Articles and videos" category on README ## 2.2.0 -- Share fragments between queries and schemas (see `fragments_glob`) [#65](https://github.com/comigor/artemis/pull/65) +- Share fragments between queries and schemas (see `fragments_glob`) [#65][pr-65] ## 2.1.4 - Add missing prefix to generated enums @@ -189,7 +201,7 @@ Set HTTP headers only when using default HTTP client. ## 0.2.0 BREAKING Completely overhaul how this works. -Artemis won't generate a full schema typing anymore. Instead, it will use the schema to generate typings from a specific query or mutation. It will also create helper functions to execute those queries. See [README](./README.md) for more info. +Artemis won't generate a full schema typing anymore. Instead, it will use the schema to generate typings from a specific query or mutation. It will also create helper functions to execute those queries. See [README][readme] for more info. This is totally a breaking change but as this library is still on alpha, I should keep it under 1.0. @@ -218,3 +230,10 @@ This is totally a breaking change but as this library is still on alpha, I shoul - Consider custom scalars - Not even compile from scratch - Lot of bugs + +[readme]: ./README.md +[pr-65]: https://github.com/comigor/artemis/pull/65 +[pr-68]: https://github.com/comigor/artemis/pull/68 +[apollo-3-ways-schema]: https://blog.apollographql.com/three-ways-to-represent-your-graphql-schema-a41f4175100d +[introspection-to-sdl-snippet]: https://gist.github.com/stubailo/041999ba5b8b15cede60b93ff9a38f53 +[introspection-to-sdl-online]: https://codesandbox.io/s/pnmoxolx4 diff --git a/README.md b/README.md index bff2f011..cabc4272 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Pub Package](https://img.shields.io/pub/v/artemis.svg)](https://pub.dev/packages/artemis) [![GitHub Actions](https://github.com/comigor/artemis/workflows/test/badge.svg)](https://github.com/comigor/artemis/actions) -Artemis is a code generator that looks for `schema.json` (GraphQL Introspection Query response data) and `*.graphql` files and builds `.dart` files typing that query, based on the schema. That's similar to what [Apollo](https://github.com/apollographql/apollo-client) does (Artemis is his sister anyway). +Artemis is a code generator that looks for `schema.graphql` (GraphQL SDL - Schema Definition Language) and `*.graphql` files and builds `.dart` files typing that query, based on the schema. That's similar to what [Apollo](https://github.com/apollographql/apollo-client) does (Artemis is his sister anyway). --- @@ -17,14 +17,14 @@ Artemis is a code generator that looks for `schema.json` (GraphQL Introspection Add the following to your `pubspec.yaml` file to be able to do code generation: ```yaml dev_dependencies: - artemis: '>=2.0.0 <3.0.0' + artemis: '>=5.0.0 <6.0.0' build_runner: ^1.5.0 json_serializable: ^3.0.0 ``` The generated code uses the following packages in run-time: ```yaml dependencies: - artemis: '>=2.0.0 <3.0.0' # only if you're using ArtemisClient! + artemis: '>=5.0.0 <6.0.0' # only if you're using ArtemisClient! json_serializable: ^3.0.0 equatable: ^0.6.1 meta: '>=1.0.0 <2.0.0' # only if you have non nullable fields @@ -75,7 +75,7 @@ targets: - lib/** - graphql/** - data/** - - schema.json + - schema.graphql ``` ### **Schema mapping** @@ -89,7 +89,7 @@ targets: options: schema_mapping: - output: lib/graphql_api.dart - schema: lib/my_graphql_schema.json + schema: lib/my_graphql_schema.graphql queries_glob: lib/**.graphql ``` diff --git a/lib/builder.dart b/lib/builder.dart index 12848e49..fd03f4dd 100644 --- a/lib/builder.dart +++ b/lib/builder.dart @@ -7,9 +7,7 @@ import 'package:gql/language.dart'; import './generator.dart'; import './generator/data.dart'; -import './generator/graphql_helpers.dart'; import './generator/print_helpers.dart'; -import './schema/graphql.dart'; import './schema/options.dart'; /// [GraphQLQueryBuilder] instance, to be used by `build_runner`. @@ -55,17 +53,6 @@ class GraphQLQueryBuilder implements Builder { r'$lib$': expectedOutputs, }; - Future _readSchemaFromPath( - BuildStep buildStep, SchemaMap schemaMap) async { - final assetStream = buildStep.findAssets(Glob(schemaMap.schema)); - final schemaFile = await assetStream.single.catchError((e) { - throw Exception('''Schema `${schemaMap.schema}` was not found! -Make sure the file exists and you've typed it conrrectly on build.yaml. -'''); - }); - return schemaFromJsonString(await buildStep.readAsString(schemaFile)); - } - @override Future build(BuildStep buildStep) async { if (options.fragmentsGlob != null) { @@ -85,7 +72,6 @@ Make sure the file exists and you've typed it conrrectly on build.yaml. for (final schemaMap in options.schemaMapping) { final buffer = StringBuffer(); final outputFileId = AssetId(buildStep.inputId.package, schemaMap.output); - final schema = await _readSchemaFromPath(buildStep, schemaMap); // Loop through all files in glob if (schemaMap.queriesGlob == null) { @@ -103,8 +89,27 @@ Make sure that `queries_glob` your build.yaml file include GraphQL queries files ) .toList(); - final libDefinition = generateLibrary(schema, schemaMap.output, gqlDocs, - options, schemaMap, fragmentsCommon); + final schemaAssetStream = buildStep.findAssets(Glob(schemaMap.schema)); + + DocumentNode gqlSchema; + + try { + gqlSchema = await schemaAssetStream + .asyncMap( + (asset) async => parseString( + await buildStep.readAsString(asset), + url: asset.path, + ), + ) + .first; + } catch (e) { + throw Exception('''Schema `${schemaMap.schema}` was not found! +Make sure the file exists and you've typed it correctly on build.yaml. +'''); + } + + final libDefinition = generateLibrary(schemaMap.output, gqlDocs, options, + schemaMap, fragmentsCommon, gqlSchema); if (onBuild != null) { onBuild(libDefinition); } diff --git a/lib/generator.dart b/lib/generator.dart index 0fca6739..55d37c98 100644 --- a/lib/generator.dart +++ b/lib/generator.dart @@ -7,8 +7,8 @@ import './generator/ephemeral_data.dart'; import './generator/data.dart'; import './generator/graphql_helpers.dart' as gql; import './generator/helpers.dart'; -import './schema/graphql.dart'; import './schema/options.dart'; +import 'visitor.dart'; typedef _OnNewClassFoundCallback = void Function(Context context); @@ -22,16 +22,22 @@ const ARTEMIS_UNKNOWN = 'ARTEMIS_UNKNOWN'; /// Generate queries definitions from a GraphQL schema and a list of queries, /// given Artemis options and schema mappings. LibraryDefinition generateLibrary( - GraphQLSchema schema, String path, List gqlDocs, GeneratorOptions options, SchemaMap schemaMap, - List fragmenDefinitionNode, + List fragmentDefinitionNode, + DocumentNode schema, ) { final queriesDefinitions = gqlDocs .map((doc) => generateQuery( - schema, path, doc, options, schemaMap, fragmenDefinitionNode)) + schema, + path, + doc, + options, + schemaMap, + fragmentDefinitionNode, + )) .toList(); final allClassesNames = queriesDefinitions.fold>( @@ -48,7 +54,8 @@ You may want to: }); final basename = p.basenameWithoutExtension(path); - final customImports = _extractCustomImports(schema.types, options); + + final customImports = _extractCustomImports(schema, options); return LibraryDefinition( basename: basename, queries: queriesDefinitions, @@ -66,36 +73,21 @@ Set _extractFragments(SelectionSetNode selectionSet, selectionSet.selections .whereType() .forEach((selection) { - final fragmentDefinition = fragmentsCommon.firstWhere( - (fragmentDifinition) => - fragmentDifinition.name.value == selection.name.value); - result.add(fragmentDefinition); + final fragmentDefinitions = fragmentsCommon.firstWhere( + (fragmentDefinition) => + fragmentDefinition.name.value == selection.name.value); + result.add(fragmentDefinitions); result.addAll( - _extractFragments(fragmentDefinition.selectionSet, fragmentsCommon)); + _extractFragments(fragmentDefinitions.selectionSet, fragmentsCommon)); }); } return result; } -GraphQLType _unwrapToType(GraphQLSchema schema, TypeNode node) { - final isList = node is ListTypeNode; - final leafNode = - (isList ? (node as ListTypeNode).type : node) as NamedTypeNode; - - final type = gql.getTypeByName(schema, leafNode.name.value, - context: 'query variables'); - - if (isList) { - return GraphQLType(kind: GraphQLTypeKind.LIST, ofType: type); - } - - return type; -} - /// Generate a query definition from a GraphQL schema and a query, given /// Artemis options and schema mappings. QueryDefinition generateQuery( - GraphQLSchema schema, + DocumentNode schema, String path, DocumentNode document, GeneratorOptions options, @@ -119,12 +111,25 @@ QueryDefinition generateQuery( final basename = p.basenameWithoutExtension(path); final queryName = operation.name?.value ?? basename; final className = ReCase(queryName).pascalCase; - final queryType = operation.type == OperationType.query - ? schema.queryType.name - : schema.mutationType.name; - final parentType = - gql.getTypeByName(schema, queryType, context: 'query/mutation root'); + final schemaVisitor = SchemaDefinitionVisitor(); + final objectVisitor = ObjectTypeDefinitionVisitor(); + + schema.accept(schemaVisitor); + schema.accept(objectVisitor); + + final rootTypeName = schemaVisitor.schemaDefinitionNode.operationTypes + .firstWhere((e) => e.operation == operation.type, orElse: () => null) + ?.type + ?.name + ?.value; + + if (rootTypeName == null) { + throw Exception( + '''No root type was found for ${operation.type} $queryName.'''); + } + + final TypeDefinitionNode parentType = objectVisitor.getByName(rootTypeName); final suffix = operation.type == OperationType.query ? 'Query' : 'Mutation'; @@ -146,7 +151,7 @@ QueryDefinition generateQuery( return QueryDefinition( queryName: queryName, - queryType: '$className\$${parentType.name}', + queryType: '$className\$${parentType.name.value}', document: document, classes: visitor.context.generatedClasses, inputs: visitor.context.inputsClasses, @@ -156,18 +161,23 @@ QueryDefinition generateQuery( } List _extractCustomImports( - List types, + DocumentNode schema, GeneratorOptions options, -) => - types - .map((GraphQLType type) { - final scalarMap = gql.getSingleScalarMap(options, type); - return scalarMap.dartType.imports.followedBy( - [scalarMap.customParserImport].where((c) => c != null)); - }) - .expand((i) => i) - .toSet() - .toList(); +) { + final typeVisitor = TypeDefinitionNodeVisitor(); + + schema.accept(typeVisitor); + + return typeVisitor.types + .map((TypeDefinitionNode type) { + final scalarMap = gql.getSingleScalarMap(options, type.name.value); + return scalarMap.dartType.imports + .followedBy([scalarMap.customParserImport].where((c) => c != null)); + }) + .expand((i) => i) + .toSet() + .toList(); +} ClassProperty _createClassProperty({ @required String fieldName, @@ -176,29 +186,46 @@ ClassProperty _createClassProperty({ @required InjectedOptions options, _OnNewClassFoundCallback onNewClassFound, }) { - final inputField = context.currentType.inputFields - .firstWhere((f) => f.name == fieldName, orElse: () => null); - final regularField = context.currentType.fields - .firstWhere((f) => f.name == fieldName, orElse: () => null); + var finalFields = []; - final fieldType = regularField?.type ?? inputField?.type; + if (context.currentType is ObjectTypeDefinitionNode) { + finalFields = (context.currentType as ObjectTypeDefinitionNode).fields; + } + + if (context.currentType is InterfaceTypeDefinitionNode) { + finalFields = (context.currentType as InterfaceTypeDefinitionNode).fields; + } + + if (context.currentType is InputObjectTypeDefinitionNode) { + finalFields = (context.currentType as InputObjectTypeDefinitionNode).fields; + } + + final regularField = finalFields.firstWhere((f) => f.name.value == fieldName, + orElse: () => null); + + final fieldType = regularField?.type; if (fieldType == null) { throw Exception( - '''Field $fieldName was not found in GraphQL type ${context.currentType.name}. + '''Field $fieldName was not found in GraphQL type ${context.currentType.name.value}. Make sure your query is correct and your schema is updated.'''); } final aliasAsClassName = fieldAlias != null ? ReCase(fieldAlias).pascalCase : null; - final nextType = - fieldType.follow().upgrade(options.schema, context: 'field node'); - final nextClassName = context.joinedName(aliasAsClassName ?? nextType.name); - final dartTypeStr = gql.buildTypeString(fieldType, options.options, - dartType: true, replaceLeafWith: nextClassName); + final nextType = gql.getTypeByName(options.schema, fieldType as Node, + context: 'field node'); + + final nextClassName = + context.joinedName(aliasAsClassName ?? nextType?.name?.value); - if (nextType.kind != GraphQLTypeKind.SCALAR && - nextType.kind != GraphQLTypeKind.ENUM && + final dartTypeStr = gql.buildTypeString( + fieldType as TypeNode, options.options, + dartType: true, replaceLeafWith: nextClassName, schema: options.schema); + + if ((nextType is ObjectTypeDefinitionNode || + nextType is UnionTypeDefinitionNode || + nextType is InterfaceTypeDefinitionNode) && onNewClassFound != null) { onNewClassFound( context.nextTypeWithSamePath( @@ -210,19 +237,22 @@ Make sure your query is correct and your schema is updated.'''); // On custom scalars String annotation; - final scalar = gql.getSingleScalarMap(options.options, nextType); - if (nextType.kind == GraphQLTypeKind.SCALAR && - scalar.customParserImport != null) { - final graphqlTypeSafeStr = gql - .buildTypeString(fieldType, options.options, dartType: false) - .replaceAll(RegExp(r'[<>]'), ''); - final dartTypeSafeStr = dartTypeStr.replaceAll(RegExp(r'[<>]'), ''); - annotation = - 'JsonKey(fromJson: fromGraphQL${dartTypeStr}ToDart$dartTypeSafeStr, toJson: fromDart${dartTypeSafeStr}ToGraphQL$graphqlTypeSafeStr)'; - } - - // On enums - else if (nextType.kind == GraphQLTypeKind.ENUM) { + if (nextType is ScalarTypeDefinitionNode) { + final scalar = gql.getSingleScalarMap(options.options, nextType.name.value); + + if (scalar.customParserImport != null && + nextType is ScalarTypeDefinitionNode && + nextType.name.value == scalar.graphQLType) { + final graphqlTypeSafeStr = gql + .buildTypeString(fieldType as TypeNode, options.options, + dartType: false, schema: options.schema) + .replaceAll(RegExp(r'[<>]'), ''); + final dartTypeSafeStr = dartTypeStr.replaceAll(RegExp(r'[<>]'), ''); + annotation = + 'JsonKey(fromJson: fromGraphQL${dartTypeStr}ToDart$dartTypeSafeStr, toJson: fromDart${dartTypeSafeStr}ToGraphQL$graphqlTypeSafeStr)'; + } + } // On enums + else if (nextType is EnumTypeDefinitionNode) { _generateEnumForType( context.nextTypeWithSamePath( nextType: nextType, @@ -230,7 +260,7 @@ Make sure your query is correct and your schema is updated.'''); ), options, ); - if (!fieldType.isList()) { + if (fieldType is! ListTypeNode) { annotation = 'JsonKey(unknownEnumValue: $dartTypeStr.$ARTEMIS_UNKNOWN)'; } } @@ -239,19 +269,18 @@ Make sure your query is correct and your schema is updated.'''); type: dartTypeStr, name: fieldAlias ?? fieldName, annotation: annotation, - isNonNull: fieldType.kind == GraphQLTypeKind.NON_NULL, + isNonNull: (fieldType as TypeNode).isNonNull, ); } void _generateEnumForType(Context context, InjectedOptions options) { - final currentType = - context.currentType.upgrade(options.schema, context: 'enum'); + final enumType = context.currentType as EnumTypeDefinitionNode; _log('<- Generated enum ${context.joinedName()}', 0); context.generatedClasses.add( EnumDefinition( name: context.joinedName(), - values: currentType.enumValues.map((eV) => eV.name).toList() + values: enumType.values.map((eV) => eV.name.value).toList() ..add(ARTEMIS_UNKNOWN), ), ); @@ -275,14 +304,17 @@ class _GeneratorVisitor extends RecursiveVisitor { super.visitSelectionSetNode(node); final possibleTypes = {}; - if (context.currentType.kind == GraphQLTypeKind.UNION || - context.currentType.kind == GraphQLTypeKind.INTERFACE) { + + if (context.currentType is UnionTypeDefinitionNode || + context.currentType is InterfaceTypeDefinitionNode) { // Filter by requested types final keys = node.selections .whereType() .map((n) => n.typeCondition.on.name.value); + final values = keys.map((t) => context.sameTypeWithNextPath().joinedName(t)); + possibleTypes.addAll(Map.fromIterables(keys, values)); _classProperties.add(ClassProperty( type: 'String', @@ -293,6 +325,7 @@ class _GeneratorVisitor extends RecursiveVisitor { )); _log('It is an union/interface of possible types $possibleTypes.'); } + final partOfUnion = context.ofUnion != null; if (partOfUnion) { _log('It is part of union ${context.ofUnion.name}.'); @@ -309,32 +342,54 @@ class _GeneratorVisitor extends RecursiveVisitor { } void _generateInputObjectClassesByType(Context context) { - final properties = context.currentType.inputFields.map((i) { - return _createClassProperty( - fieldName: i.name, - context: context.sameTypeWithNextPath(), - options: options, - onNewClassFound: (nextContext) { - _generateInputObjectClassesByType(nextContext); - }, - ); - }).toList(); + var properties = []; + + if (context.currentType is ObjectTypeDefinitionNode) { + properties = + (context.currentType as ObjectTypeDefinitionNode).fields.map((i) { + return _createClassProperty( + fieldName: i.name.value, + context: context.sameTypeWithNextPath(), + options: options, + onNewClassFound: (nextContext) { + _generateInputObjectClassesByType(nextContext); + }, + ); + }).toList(); + } + + if (context.currentType is InputObjectTypeDefinitionNode) { + properties = (context.currentType as InputObjectTypeDefinitionNode) + .fields + .map((i) { + return _createClassProperty( + fieldName: i.name.value, + context: context.sameTypeWithNextPath(), + options: options, + onNewClassFound: (nextContext) { + _generateInputObjectClassesByType(nextContext); + }, + ); + }).toList(); + } _log('<- Generated input class ${context.joinedName()}.', 0); context.generatedClasses.add(ClassDefinition( name: context.joinedName(), - properties: properties, + properties: properties as List, isInput: true, )); } @override void visitVariableDefinitionNode(VariableDefinitionNode node) { - final varType = _unwrapToType(options.schema, node.type); - final nextClassName = context.joinedName(varType.name); + final nextClassName = context.joinedName((node.type is ListTypeNode) + ? null + : (node.type as NamedTypeNode).name.value); + + final dartTypeStr = gql.buildTypeString(node.type, options.options, + dartType: true, replaceLeafWith: nextClassName, schema: options.schema); - final dartTypeStr = gql.buildTypeString(varType, options.options, - dartType: true, replaceLeafWith: nextClassName); context.inputsClasses.add(QueryInput( type: dartTypeStr, name: node.variable.name.value, @@ -343,12 +398,18 @@ class _GeneratorVisitor extends RecursiveVisitor { _log('Found new input ${node.variable.name.value} (-> $dartTypeStr).'); - final leafType = varType.follow().upgrade(options.schema); - final nextContext = context.nextTypeWithSamePath(nextType: leafType); - if (leafType.kind == GraphQLTypeKind.INPUT_OBJECT) { - _generateInputObjectClassesByType(nextContext); - } else if (leafType.kind == GraphQLTypeKind.ENUM) { - _generateEnumForType(nextContext, options); + final leafType = + gql.getTypeByName(options.schema, node.type, context: 'field node'); + + if (leafType is TypeDefinitionNode) { + final nextContext = context.nextTypeWithSamePath(nextType: leafType); + + if (leafType is ObjectTypeDefinitionNode || + leafType is InputObjectTypeDefinitionNode) { + _generateInputObjectClassesByType(nextContext); + } else if (leafType is EnumTypeDefinitionNode) { + _generateEnumForType(nextContext, options); + } } } @@ -357,8 +418,7 @@ class _GeneratorVisitor extends RecursiveVisitor { context.fragments.add(node); final partName = '${ReCase(node.name.value).pascalCase}Mixin'; - final fragmentOnClassName = node.typeCondition.on.name.value; - final nextType = gql.getTypeByName(options.schema, fragmentOnClassName, + final nextType = gql.getTypeByName(options.schema, node.typeCondition.on, context: 'fragment definition'); final visitor = _GeneratorVisitor( @@ -391,11 +451,11 @@ class _GeneratorVisitor extends RecursiveVisitor { @override void visitInlineFragmentNode(InlineFragmentNode node) { - final onTypeName = node.typeCondition.on.name.value; - final nextType = gql.getTypeByName(options.schema, onTypeName, + final nextType = gql.getTypeByName(options.schema, node.typeCondition.on, context: 'inline fragment'); - _log('Fragment spread on ${nextType.name} (context: ${context.path}).'); + _log( + 'Fragment spread on ${nextType.name.value} (context: ${context.path}).'); final visitor = _GeneratorVisitor( context: context.next( @@ -418,7 +478,7 @@ class _GeneratorVisitor extends RecursiveVisitor { } _log( - 'Visiting $fieldName ${node.alias?.value != null ? '(alias: ${node.alias.value})' : ''} on ${context.currentType.name} (context: ${context.path}).'); + 'Visiting $fieldName ${node.alias?.value != null ? '(alias: ${node.alias.value})' : ''} on ${context.currentType.name.value} (context: ${context.path}).'); final property = _createClassProperty( fieldName: fieldName, @@ -441,7 +501,7 @@ class _GeneratorVisitor extends RecursiveVisitor { .sameTypeWithFirstPath() .joinedName('${ReCase(node.name.value).pascalCase}Mixin'); _log( - 'Spreading fragment $fragmentName into GraphQL type ${context.currentType.name} (context: ${context.path}).'); + 'Spreading fragment $fragmentName into GraphQL type ${context.currentType.name.value} (context: ${context.path}).'); _mixins.add(fragmentName); } } diff --git a/lib/generator/ephemeral_data.dart b/lib/generator/ephemeral_data.dart index 71c53d1b..870fb28c 100644 --- a/lib/generator/ephemeral_data.dart +++ b/lib/generator/ephemeral_data.dart @@ -2,7 +2,6 @@ import 'package:meta/meta.dart'; import 'package:gql/ast.dart'; import './data.dart'; -import '../schema/graphql.dart'; import '../schema/options.dart'; /// References options while on [_GeneratorVisitor] iterations. @@ -14,8 +13,8 @@ class InjectedOptions { @required this.schemaMap, }); - /// The [GraphQLSchema] parsed from `build.yaml` configuration. - final GraphQLSchema schema; + /// The [DocumentNode] parsed from `build.yaml` configuration. + final DocumentNode schema; /// Other options parsed from `build.yaml` configuration. final GeneratorOptions options; @@ -40,11 +39,11 @@ class Context { /// The path of data we're currently processing. final List path; - /// The [GraphQLType] we're currently processing. - final GraphQLType currentType; + /// The [TypeDefinitionNode] we're currently processing. + final TypeDefinitionNode currentType; - /// If part of an union type, which [GraphQLType] it represents. - final GraphQLType ofUnion; + /// If part of an union type, which [TypeDefinitionNode] it represents. + final TypeDefinitionNode ofUnion; /// A string to replace the current class name. final String alias; @@ -60,12 +59,12 @@ class Context { /// Returns the full class name with joined path. String joinedName([String name]) => - '${path.join(r'$')}\$${name ?? alias ?? currentType.name}'; + '${path.join(r'$')}\$${name ?? alias ?? currentType.name.value}'; /// Returns a copy of this context, on the same path, but with a new type. Context nextTypeWithSamePath({ - @required GraphQLType nextType, - GraphQLType ofUnion, + @required TypeDefinitionNode nextType, + TypeDefinitionNode ofUnion, String alias, List generatedClasses, List inputsClasses, @@ -83,15 +82,15 @@ class Context { /// Returns a copy of this context, with a new type on a new path. Context next({ - @required GraphQLType nextType, - GraphQLType ofUnion, + @required TypeDefinitionNode nextType, + TypeDefinitionNode ofUnion, String alias, List generatedClasses, List inputsClasses, List fragments, }) => Context( - path: path.followedBy([alias ?? currentType.name]).toList(), + path: path.followedBy([alias ?? currentType.name.value]).toList(), currentType: nextType, ofUnion: ofUnion ?? this.ofUnion, alias: alias ?? this.alias, @@ -102,14 +101,14 @@ class Context { /// Returns a copy of this context, with the same type, but on a new path. Context sameTypeWithNextPath({ - GraphQLType ofUnion, + TypeDefinitionNode ofUnion, String alias, List generatedClasses, List inputsClasses, List fragments, }) => Context( - path: path.followedBy([alias ?? currentType.name]).toList(), + path: path.followedBy([alias ?? currentType.name.value]).toList(), currentType: currentType, ofUnion: ofUnion ?? this.ofUnion, alias: alias ?? this.alias, @@ -120,7 +119,7 @@ class Context { /// Returns a copy of this context, with the same type, but on the first path. Context sameTypeWithFirstPath({ - GraphQLType ofUnion, + TypeDefinitionNode ofUnion, List generatedClasses, List inputsClasses, List fragments, diff --git a/lib/generator/graphql_helpers.dart b/lib/generator/graphql_helpers.dart index 752754da..908b8f21 100644 --- a/lib/generator/graphql_helpers.dart +++ b/lib/generator/graphql_helpers.dart @@ -1,59 +1,66 @@ -import 'dart:convert'; -import '../schema/graphql.dart'; +import 'package:artemis/visitor.dart'; +import 'package:gql/ast.dart'; + import '../schema/options.dart'; -/// Get a full [GraphQLType] from its canonical name. -GraphQLType getTypeByName(GraphQLSchema schema, String name, {String context}) { - if (_defaultScalarMapping.containsKey(name)) { - return GraphQLType( - name: name, - kind: GraphQLTypeKind.SCALAR, - ); +/// Get a full [TypeDefinitionNode] from its name. +TypeDefinitionNode getTypeByName(DocumentNode schema, Node name, + {String context}) { + NamedTypeNode namedNode; + + if (name is NamedTypeNode) { + namedNode = name; } - return schema.types.firstWhere((t) => t.name == name, - orElse: () => throw Exception( - 'Type $name not found on schema (on `$context` context).')); -} -/// "Follow" a type to find the leaf scalar or type. -GraphQLType followType(GraphQLType type) { - switch (type.kind) { - case GraphQLTypeKind.LIST: - case GraphQLTypeKind.NON_NULL: - return followType(type.ofType); - default: - return type; + if (name is ListTypeNode) { + namedNode = name.type as NamedTypeNode; } + + final typeVisitor = TypeDefinitionNodeVisitor(); + schema.accept(typeVisitor); + + final type = typeVisitor.getByName(namedNode.name.value); + + return type; } -/// Find if this type is eventually a list. -bool isEventuallyList(GraphQLType type) { - switch (type.kind) { - case GraphQLTypeKind.LIST: - return true; - case GraphQLTypeKind.NON_NULL: - return isEventuallyList(type.ofType); - default: - return false; +/// Build a string representing a Dart type, given a GraphQL type. +String buildTypeString( + Node node, + GeneratorOptions options, { + bool dartType = true, + String replaceLeafWith, + String prefix = '', + DocumentNode schema, +}) { + if (node is NamedTypeNode) { + final typeVisitor = TypeDefinitionNodeVisitor(); + schema.accept(typeVisitor); + final type = typeVisitor.getByName(node.name.value); + + final scalar = getSingleScalarMap(options, node.name.value); + final dartTypeValue = dartType ? scalar.dartType.name : scalar.graphQLType; + + if (type != null) { + if (type is ScalarTypeDefinitionNode) { + return dartTypeValue; + } + + if (replaceLeafWith != null) { + return '$prefix$replaceLeafWith'; + } else { + return '$prefix${type.name.value}'; + } + } + + return dartTypeValue; } -} -/// Build a string repesenting a Dart type, given a GraphQL type. -String buildTypeString(GraphQLType type, GeneratorOptions options, - {bool dartType = true, String replaceLeafWith}) { - switch (type.kind) { - case GraphQLTypeKind.LIST: - return 'List<${buildTypeString(type.ofType, options, dartType: dartType, replaceLeafWith: replaceLeafWith)}>'; - case GraphQLTypeKind.NON_NULL: - return buildTypeString(type.ofType, options, - dartType: dartType, replaceLeafWith: replaceLeafWith); - case GraphQLTypeKind.SCALAR: - final scalar = getSingleScalarMap(options, type); - return dartType ? scalar.dartType.name : scalar.graphQLType; - default: - if (replaceLeafWith != null) return replaceLeafWith; - return type.name; + if (node is ListTypeNode) { + return 'List<${buildTypeString(node.type, options, dartType: dartType, replaceLeafWith: replaceLeafWith, prefix: prefix, schema: schema)}>'; } + + throw Exception('Unable to build type string'); } Map _defaultScalarMapping = { @@ -68,15 +75,11 @@ Map _defaultScalarMapping = { }; /// Retrieve a scalar mapping of a type. -ScalarMap getSingleScalarMap(GeneratorOptions options, GraphQLType type) => +ScalarMap getSingleScalarMap(GeneratorOptions options, String type) => options.scalarMapping .followedBy(_defaultScalarMapping.values) - .firstWhere((m) => m.graphQLType == type.name, + .firstWhere((m) => m.graphQLType == type, orElse: () => ScalarMap( - graphQLType: type.name, - dartType: DartType(name: type.name), + graphQLType: type, + dartType: DartType(name: type), )); - -/// Instantiates a schema from a JSON string. -GraphQLSchema schemaFromJsonString(String jsonS) => - GraphQLSchema.fromJson(json.decode(jsonS) as Map); diff --git a/lib/schema/graphql.dart b/lib/schema/graphql.dart deleted file mode 100644 index 5c253470..00000000 --- a/lib/schema/graphql.dart +++ /dev/null @@ -1,348 +0,0 @@ -import 'package:artemis/generator/graphql_helpers.dart'; -import 'package:json_annotation/json_annotation.dart'; - -// I can't use the default json_serializable flow because the artemis generator -// would crash when importing graphql.dart file. -part 'graphql.g2.dart'; - -// https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js -// https://github.com/graphql/graphql-js/blob/master/src/type/introspection.js -// https://gist.github.com/Igor1201/8bf5b3ca05d853fe93e685c53f2f726d - -/// Represents a GraphQL schema. -@JsonSerializable() -class GraphQLSchema { - /// A list of GraphQL types in this schema. - final List types; - - /// The GraphQL query type in this schema. - final GraphQLType queryType; - - /// The GraphQL mutation type in this schema. - final GraphQLType mutationType; - - /// The GraphQL subscription type in this schema. - final GraphQLType subscriptionType; - - /// A list of GraphQL directives in this schema. - final List directives; - - /// Instantiates a GraphQL schema. - GraphQLSchema({ - List types, - this.queryType, - this.mutationType, - this.subscriptionType, - List directives, - }) : types = types ?? [], - directives = directives ?? []; - - /// Build a GraphQL schema from a JSON map. - factory GraphQLSchema.fromJson(Map json) { - // Support finding '__schema' nested under 'data' or not. - // Graphql codegen generates the __schema field at the top level. - assert( - json != null && - ((json['data'] != null && json['data']['__schema'] != null) || - json['__schema'] != null), - 'JSON schema file must be the output of an Introspection Query.'); - - if (json['__schema'] != null) { - return _$GraphQLSchemaFromJson(json['__schema'] as Map); - } - - return _$GraphQLSchemaFromJson( - json['data']['__schema'] as Map); - } - - /// Convert this GraphQL schema instance to JSON. - Map toJson() => _$GraphQLSchemaToJson(this); -} - -/// Represents a GraphQL directive. -@JsonSerializable() -class GraphQLDirective { - /// The directive name. - final String name; - - /// The directive description. - final String description; - - /// A list of locations of this directive. - List locations; - - /// A list of arguments of this directive. - List args; - - /// Instantiates a GraphQL directive. - GraphQLDirective({ - this.name, - this.description, - List locations, - List args, - }) : args = args ?? [], - locations = locations ?? []; - - /// Build a GraphQL directive from a JSON map. - factory GraphQLDirective.fromJson(Map json) => - _$GraphQLDirectiveFromJson(json); - - /// Convert this GraphQL directive instance to JSON. - Map toJson() => _$GraphQLDirectiveToJson(this); -} - -/// An enum of GraphQL directive locations. -enum GraphQLDirectiveLocation { - /// QUERY - QUERY, - - /// MUTATION - MUTATION, - - /// SUBSCRIPTION - SUBSCRIPTION, - - /// FIELD - FIELD, - - /// FRAGMENT_DEFINITION - FRAGMENT_DEFINITION, - - /// FRAGMENT_SPREAD - FRAGMENT_SPREAD, - - /// INLINE_FRAGMENT - INLINE_FRAGMENT, - - /// VARIABLE_DEFINITION - VARIABLE_DEFINITION, - - /// SCHEMA - SCHEMA, - - /// SCALAR - SCALAR, - - /// OBJECT - OBJECT, - - /// FIELD_DEFINITION - FIELD_DEFINITION, - - /// ARGUMENT_DEFINITION - ARGUMENT_DEFINITION, - - /// INTERFACE - INTERFACE, - - /// UNION - UNION, - - /// ENUM - ENUM, - - /// ENUM_VALUE - ENUM_VALUE, - - /// INPUT_OBJECT - INPUT_OBJECT, - - /// INPUT_FIELD_DEFINITION - INPUT_FIELD_DEFINITION, -} - -/// Represents a GraphQL type. -@JsonSerializable() -class GraphQLType { - /// Type kind. - final GraphQLTypeKind kind; - - /// Type name. - final String name; - - /// Type description. - final String description; - - /// Type fields. - final List fields; - - /// Type interfaces. - final List interfaces; - - /// Possible super type. - final List possibleTypes; - - /// Possible enum values. - final List enumValues; - - /// Input fields. - final List inputFields; - - /// Chain of types this follows. - final GraphQLType ofType; - - /// Instantiates a GraphQL type. - GraphQLType({ - this.kind, - this.name, - this.description, - List fields, - List interfaces, - List possibleTypes, - List enumValues, - List inputFields, - this.ofType, - }) : fields = fields ?? [], - inputFields = inputFields ?? [], - interfaces = interfaces ?? [], - enumValues = enumValues ?? [], - possibleTypes = possibleTypes ?? []; - - /// Build a GraphQL type from a JSON map. - factory GraphQLType.fromJson(Map json) => - _$GraphQLTypeFromJson(json); - - /// Convert this GraphQL type instance to JSON. - Map toJson() => _$GraphQLTypeToJson(this); - - /// Get this leaf type. - GraphQLType follow() => followType(this); - - /// Get if this type is a list. - bool isList() => isEventuallyList(this); - - /// Upgrade this type to the full type definition from schema. - GraphQLType upgrade(GraphQLSchema schema, {String context}) => - getTypeByName(schema, name, context: context); -} - -/// Defines kinds of GraphQL types. -enum GraphQLTypeKind { - /// Ths most basic (leaf) type on GraphQL. - SCALAR, - - /// Objects are a collection of other objects to fetch data from server. - OBJECT, - - /// An Interface is an abstract type that includes a certain set of fields - /// that a type must include to implement the interface. - INTERFACE, - - /// Union types are very similar to interfaces, but they don't get to - /// specify any common fields between the types - UNION, - - /// Enum is a special kind of scalar that is restricted to a particular set - /// of allowed values. - ENUM, - - /// Complex objects to be sent to server. - INPUT_OBJECT, - - /// List type modifier. - LIST, - - /// Non-null type modifier. - NON_NULL, -} - -/// Represents a GraphQL field. -@JsonSerializable() -class GraphQLField { - /// Field name. - final String name; - - /// Field description. - final String description; - - /// Field arguments. - final List args; - - /// Field type. - final GraphQLType type; - - /// If field is deprecated. - final bool isDeprecated; - - /// Why this field is deprecated. - final String deprecatedReason; - - /// Instantiates a GraphQL field. - GraphQLField({ - this.name, - this.description, - List args, - this.type, - this.isDeprecated, - this.deprecatedReason, - }) : args = args ?? []; - - /// Build a GraphQL field from a JSON map. - factory GraphQLField.fromJson(Map json) => - _$GraphQLFieldFromJson(json); - - /// Convert this GraphQL field instance to JSON. - Map toJson() => _$GraphQLFieldToJson(this); -} - -/// Represents a GraphQL input value. -@JsonSerializable() -class GraphQLInputValue { - /// Input value name. - final String name; - - /// Input value description. - final String description; - - /// Input value type. - final GraphQLType type; - - /// Default value for this input type. - final String defaultValue; - - /// Instantiates a GraphQL input value. - GraphQLInputValue({ - this.name, - this.description, - this.type, - this.defaultValue, - }); - - /// Build a GraphQL input value from a JSON map. - factory GraphQLInputValue.fromJson(Map json) => - _$GraphQLInputValueFromJson(json); - - /// Convert this GraphQL input value instance to JSON. - Map toJson() => _$GraphQLInputValueToJson(this); -} - -/// Represents a GraphQL enum. -@JsonSerializable() -class GraphQLEnumValue { - /// Enum name. - final String name; - - /// Enum description. - final String description; - - /// If this enum is deprecated. - final bool isDeprecated; - - /// Why this enum is deprecated. - final String deprecatedReason; - - /// Instantiates a GraphQL enum value. - GraphQLEnumValue({ - this.name, - this.description, - this.isDeprecated, - this.deprecatedReason, - }); - - /// Build a GraphQL enum value from a JSON map. - factory GraphQLEnumValue.fromJson(Map json) => - _$GraphQLEnumValueFromJson(json); - - /// Convert this GraphQL enum value instance to JSON. - Map toJson() => _$GraphQLEnumValueToJson(this); -} diff --git a/lib/schema/graphql.g2.dart b/lib/schema/graphql.g2.dart deleted file mode 100644 index 4d9dad2f..00000000 --- a/lib/schema/graphql.g2.dart +++ /dev/null @@ -1,226 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'graphql.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -GraphQLSchema _$GraphQLSchemaFromJson(Map json) { - return GraphQLSchema( - types: (json['types'] as List) - ?.map((e) => e == null - ? null - : GraphQLType.fromJson(e as Map)) - ?.toList(), - queryType: json['queryType'] == null - ? null - : GraphQLType.fromJson(json['queryType'] as Map), - mutationType: json['mutationType'] == null - ? null - : GraphQLType.fromJson(json['mutationType'] as Map), - subscriptionType: json['subscriptionType'] == null - ? null - : GraphQLType.fromJson( - json['subscriptionType'] as Map), - directives: (json['directives'] as List) - ?.map((e) => e == null - ? null - : GraphQLDirective.fromJson(e as Map)) - ?.toList()); -} - -Map _$GraphQLSchemaToJson(GraphQLSchema instance) => - { - 'types': instance.types, - 'queryType': instance.queryType, - 'mutationType': instance.mutationType, - 'subscriptionType': instance.subscriptionType, - 'directives': instance.directives - }; - -GraphQLDirective _$GraphQLDirectiveFromJson(Map json) { - return GraphQLDirective( - name: json['name'] as String, - description: json['description'] as String, - locations: (json['locations'] as List) - ?.map( - (e) => _$enumDecodeNullable(_$GraphQLDirectiveLocationEnumMap, e)) - ?.toList(), - args: (json['args'] as List) - ?.map((e) => e == null - ? null - : GraphQLInputValue.fromJson(e as Map)) - ?.toList()); -} - -Map _$GraphQLDirectiveToJson(GraphQLDirective instance) => - { - 'name': instance.name, - 'description': instance.description, - 'locations': instance.locations - ?.map((e) => _$GraphQLDirectiveLocationEnumMap[e]) - ?.toList(), - 'args': instance.args - }; - -T _$enumDecode(Map enumValues, dynamic source) { - if (source == null) { - throw ArgumentError('A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}'); - } - return enumValues.entries - .singleWhere((e) => e.value == source, - orElse: () => throw ArgumentError( - '`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}')) - .key; -} - -T _$enumDecodeNullable(Map enumValues, dynamic source) { - if (source == null) { - return null; - } - return _$enumDecode(enumValues, source); -} - -const _$GraphQLDirectiveLocationEnumMap = { - GraphQLDirectiveLocation.QUERY: 'QUERY', - GraphQLDirectiveLocation.MUTATION: 'MUTATION', - GraphQLDirectiveLocation.SUBSCRIPTION: 'SUBSCRIPTION', - GraphQLDirectiveLocation.FIELD: 'FIELD', - GraphQLDirectiveLocation.FRAGMENT_DEFINITION: 'FRAGMENT_DEFINITION', - GraphQLDirectiveLocation.FRAGMENT_SPREAD: 'FRAGMENT_SPREAD', - GraphQLDirectiveLocation.INLINE_FRAGMENT: 'INLINE_FRAGMENT', - GraphQLDirectiveLocation.VARIABLE_DEFINITION: 'VARIABLE_DEFINITION', - GraphQLDirectiveLocation.SCHEMA: 'SCHEMA', - GraphQLDirectiveLocation.SCALAR: 'SCALAR', - GraphQLDirectiveLocation.OBJECT: 'OBJECT', - GraphQLDirectiveLocation.FIELD_DEFINITION: 'FIELD_DEFINITION', - GraphQLDirectiveLocation.ARGUMENT_DEFINITION: 'ARGUMENT_DEFINITION', - GraphQLDirectiveLocation.INTERFACE: 'INTERFACE', - GraphQLDirectiveLocation.UNION: 'UNION', - GraphQLDirectiveLocation.ENUM: 'ENUM', - GraphQLDirectiveLocation.ENUM_VALUE: 'ENUM_VALUE', - GraphQLDirectiveLocation.INPUT_OBJECT: 'INPUT_OBJECT', - GraphQLDirectiveLocation.INPUT_FIELD_DEFINITION: 'INPUT_FIELD_DEFINITION' -}; - -GraphQLType _$GraphQLTypeFromJson(Map json) { - return GraphQLType( - kind: _$enumDecodeNullable(_$GraphQLTypeKindEnumMap, json['kind']), - name: json['name'] as String, - description: json['description'] as String, - fields: (json['fields'] as List) - ?.map((e) => e == null - ? null - : GraphQLField.fromJson(e as Map)) - ?.toList(), - interfaces: (json['interfaces'] as List) - ?.map((e) => e == null - ? null - : GraphQLType.fromJson(e as Map)) - ?.toList(), - possibleTypes: (json['possibleTypes'] as List) - ?.map((e) => e == null - ? null - : GraphQLType.fromJson(e as Map)) - ?.toList(), - enumValues: (json['enumValues'] as List) - ?.map((e) => e == null - ? null - : GraphQLEnumValue.fromJson(e as Map)) - ?.toList(), - inputFields: (json['inputFields'] as List) - ?.map((e) => e == null - ? null - : GraphQLInputValue.fromJson(e as Map)) - ?.toList(), - ofType: json['ofType'] == null - ? null - : GraphQLType.fromJson(json['ofType'] as Map)); -} - -Map _$GraphQLTypeToJson(GraphQLType instance) => - { - 'kind': _$GraphQLTypeKindEnumMap[instance.kind], - 'name': instance.name, - 'description': instance.description, - 'fields': instance.fields, - 'interfaces': instance.interfaces, - 'possibleTypes': instance.possibleTypes, - 'enumValues': instance.enumValues, - 'inputFields': instance.inputFields, - 'ofType': instance.ofType - }; - -const _$GraphQLTypeKindEnumMap = { - GraphQLTypeKind.SCALAR: 'SCALAR', - GraphQLTypeKind.OBJECT: 'OBJECT', - GraphQLTypeKind.INTERFACE: 'INTERFACE', - GraphQLTypeKind.UNION: 'UNION', - GraphQLTypeKind.ENUM: 'ENUM', - GraphQLTypeKind.INPUT_OBJECT: 'INPUT_OBJECT', - GraphQLTypeKind.LIST: 'LIST', - GraphQLTypeKind.NON_NULL: 'NON_NULL' -}; - -GraphQLField _$GraphQLFieldFromJson(Map json) { - return GraphQLField( - name: json['name'] as String, - description: json['description'] as String, - args: (json['args'] as List) - ?.map((e) => e == null - ? null - : GraphQLInputValue.fromJson(e as Map)) - ?.toList(), - type: json['type'] == null - ? null - : GraphQLType.fromJson(json['type'] as Map), - isDeprecated: json['isDeprecated'] as bool, - deprecatedReason: json['deprecatedReason'] as String); -} - -Map _$GraphQLFieldToJson(GraphQLField instance) => - { - 'name': instance.name, - 'description': instance.description, - 'args': instance.args, - 'type': instance.type, - 'isDeprecated': instance.isDeprecated, - 'deprecatedReason': instance.deprecatedReason - }; - -GraphQLInputValue _$GraphQLInputValueFromJson(Map json) { - return GraphQLInputValue( - name: json['name'] as String, - description: json['description'] as String, - type: json['type'] == null - ? null - : GraphQLType.fromJson(json['type'] as Map), - defaultValue: json['defaultValue'] as String); -} - -Map _$GraphQLInputValueToJson(GraphQLInputValue instance) => - { - 'name': instance.name, - 'description': instance.description, - 'type': instance.type, - 'defaultValue': instance.defaultValue - }; - -GraphQLEnumValue _$GraphQLEnumValueFromJson(Map json) { - return GraphQLEnumValue( - name: json['name'] as String, - description: json['description'] as String, - isDeprecated: json['isDeprecated'] as bool, - deprecatedReason: json['deprecatedReason'] as String); -} - -Map _$GraphQLEnumValueToJson(GraphQLEnumValue instance) => - { - 'name': instance.name, - 'description': instance.description, - 'isDeprecated': instance.isDeprecated, - 'deprecatedReason': instance.deprecatedReason - }; diff --git a/lib/schema/options.dart b/lib/schema/options.dart index 2d7932d9..4709d3ed 100644 --- a/lib/schema/options.dart +++ b/lib/schema/options.dart @@ -113,7 +113,7 @@ class SchemaMap { @JsonKey(defaultValue: '__typename') final String typeNameField; - /// Instatiates a schema mapping. + /// Instantiates a schema mapping. SchemaMap({ this.output, this.schema, diff --git a/lib/visitor.dart b/lib/visitor.dart new file mode 100644 index 00000000..9a1256da --- /dev/null +++ b/lib/visitor.dart @@ -0,0 +1,129 @@ +import 'package:gql/ast.dart'; + +/// Visits all object definition nodes recursively +class ObjectTypeDefinitionVisitor extends RecursiveVisitor { + /// Stores all object definition nodes + Iterable types = []; + + @override + void visitObjectTypeDefinitionNode( + ObjectTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitObjectTypeDefinitionNode(node); + } + + /// Gets object type definition node by operation name + ObjectTypeDefinitionNode getByName(String name) { + final type = types.where((type) => type.name.value == name); + + if (type.isNotEmpty) { + return type.first; + } + + return null; + } +} + +/// Visits the schema definition node. +class SchemaDefinitionVisitor extends RecursiveVisitor { + /// Store the schema definition. + SchemaDefinitionNode schemaDefinitionNode; + + @override + void visitSchemaDefinitionNode( + SchemaDefinitionNode node, + ) { + schemaDefinitionNode = node; + } +} + +/// Visits all operation definition nodes recursively +class OperationTypeDefinitionNodeVisitor extends RecursiveVisitor { + /// Stores all operation definition nodes + Iterable types = []; + + @override + void visitOperationTypeDefinitionNode( + OperationTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitOperationTypeDefinitionNode(node); + } + + /// Gets operation type definition node by operation name + OperationTypeDefinitionNode getByType(OperationType operationType) { + final type = types.where((type) => type.operation == operationType); + + if (type.isNotEmpty) { + return type.first; + } + + return null; + } +} + +/// Visits all type definition nodes recursively +class TypeDefinitionNodeVisitor extends RecursiveVisitor { + /// Stores all type definition nodes + Iterable types = []; + + @override + void visitObjectTypeDefinitionNode( + ObjectTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitObjectTypeDefinitionNode(node); + } + + @override + void visitScalarTypeDefinitionNode( + ScalarTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitScalarTypeDefinitionNode(node); + } + + @override + void visitInterfaceTypeDefinitionNode( + InterfaceTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitInterfaceTypeDefinitionNode(node); + } + + @override + void visitUnionTypeDefinitionNode( + UnionTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitUnionTypeDefinitionNode(node); + } + + @override + void visitInputObjectTypeDefinitionNode( + InputObjectTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitInputObjectTypeDefinitionNode(node); + } + + @override + void visitEnumTypeDefinitionNode( + EnumTypeDefinitionNode node, + ) { + types = types.followedBy([node]); + super.visitEnumTypeDefinitionNode(node); + } + + /// Gets type definition node by type name + TypeDefinitionNode getByName(String name) { + final type = types.where((type) => type.name.value == name); + + if (type.isNotEmpty) { + return type.first; + } + + return null; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 1ce05d33..72a134e5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: artemis -version: 4.0.2 +version: 5.0.0 authors: - Igor Borges diff --git a/test/helpers.dart b/test/helpers.dart index 439b3e34..6d2f01b5 100644 --- a/test/helpers.dart +++ b/test/helpers.dart @@ -1,8 +1,5 @@ -import 'dart:convert'; - import 'package:artemis/builder.dart'; import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:build/build.dart'; import 'package:build_test/build_test.dart'; import 'package:logging/logging.dart'; @@ -11,10 +8,6 @@ import 'package:test/test.dart'; const IS_DEBUG = true; -String jsonFromSchema(GraphQLSchema schema) => json.encode({ - 'data': {'__schema': schema.toJson()} - }); - void debug(LogRecord log) { if (IS_DEBUG) print(log); } @@ -23,21 +16,19 @@ Future testGenerator({ @required String query, @required LibraryDefinition libraryDefinition, @required String generatedFile, - GraphQLSchema typedSchema, - String stringSchema, + @required String schema, bool generateHelpers = false, Map builderOptionsMap = const {}, Map sourceAssetsMap = const {}, }) async { - assert((typedSchema ?? stringSchema) != null); - final schema = stringSchema ?? jsonFromSchema(typedSchema); + assert((schema) != null); final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ if (!generateHelpers) 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.graphql', + 'queries_glob': 'queries/**.graphql', 'output': 'lib/query.dart', } ], @@ -52,8 +43,8 @@ Future testGenerator({ return await testBuilder( anotherBuilder, { - 'a|api.schema.json': schema, - 'a|query.graphql': query, + 'a|api.schema.graphql': schema, + 'a|queries/query.graphql': query, ...sourceAssetsMap, }, outputs: { diff --git a/test/query_generator/aliases/alias_on_leaves_test.dart b/test/query_generator/aliases/alias_on_leaves_test.dart index 8a28ffad..7b4b9482 100644 --- a/test/query_generator/aliases/alias_on_leaves_test.dart +++ b/test/query_generator/aliases/alias_on_leaves_test.dart @@ -1,5 +1,5 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; +import 'package:gql/language.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -10,9 +10,28 @@ void main() { 'Leaves can be aliased', () async => testGenerator( query: query, + schema: r''' + schema { + query: Response + } + + type Response { + s: String + o: SomeObject + ob: [SomeObject] + } + + type SomeObject { + e: MyEnum + } + + enum MyEnum { + A + B + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, ), ); }); @@ -27,47 +46,25 @@ const query = r''' } '''; -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM, enumValues: [ - GraphQLEnumValue(name: 'A'), - GraphQLEnumValue(name: 'B'), - ]), - GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 's', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'o', - type: - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT)), - ]), - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 'e', - type: GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM)), - ]), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( + document: parseString(query), queryName: r'some_query', - queryType: r'SomeQuery$Query', + queryType: r'SomeQuery$Response', classes: [ EnumDefinition( - name: r'SomeQuery$Query$SomeObject$ThisIsAnEnum', + name: r'SomeQuery$Response$SomeObject$ThisIsAnEnum', values: [r'A', r'B', r'ARTEMIS_UNKNOWN']), ClassDefinition( - name: r'SomeQuery$Query$SomeObject', + name: r'SomeQuery$Response$SomeObject', properties: [ ClassProperty( - type: r'SomeQuery$Query$SomeObject$ThisIsAnEnum', + type: r'SomeQuery$Response$SomeObject$ThisIsAnEnum', name: r'thisIsAnEnum', isOverride: false, annotation: - r'JsonKey(unknownEnumValue: SomeQuery$Query$SomeObject$ThisIsAnEnum.ARTEMIS_UNKNOWN)', + r'JsonKey(unknownEnumValue: SomeQuery$Response$SomeObject$ThisIsAnEnum.ARTEMIS_UNKNOWN)', isNonNull: false, isResolveType: false) ], @@ -75,7 +72,7 @@ final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ typeNameField: r'__typename', isInput: false), ClassDefinition( - name: r'SomeQuery$Query', + name: r'SomeQuery$Response', properties: [ ClassProperty( type: r'String', @@ -84,7 +81,7 @@ final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ isNonNull: false, isResolveType: false), ClassProperty( - type: r'SomeQuery$Query$SomeObject', + type: r'SomeQuery$Response$SomeObject', name: r'o', isOverride: false, isNonNull: false, @@ -106,38 +103,39 @@ import 'package:gql/ast.dart'; part 'query.g.dart'; @JsonSerializable(explicitToJson: true) -class SomeQuery$Query$SomeObject with EquatableMixin { - SomeQuery$Query$SomeObject(); +class SomeQuery$Response$SomeObject with EquatableMixin { + SomeQuery$Response$SomeObject(); - factory SomeQuery$Query$SomeObject.fromJson(Map json) => - _$SomeQuery$Query$SomeObjectFromJson(json); + factory SomeQuery$Response$SomeObject.fromJson(Map json) => + _$SomeQuery$Response$SomeObjectFromJson(json); @JsonKey( - unknownEnumValue: SomeQuery$Query$SomeObject$ThisIsAnEnum.ARTEMIS_UNKNOWN) - SomeQuery$Query$SomeObject$ThisIsAnEnum thisIsAnEnum; + unknownEnumValue: + SomeQuery$Response$SomeObject$ThisIsAnEnum.ARTEMIS_UNKNOWN) + SomeQuery$Response$SomeObject$ThisIsAnEnum thisIsAnEnum; @override List get props => [thisIsAnEnum]; - Map toJson() => _$SomeQuery$Query$SomeObjectToJson(this); + Map toJson() => _$SomeQuery$Response$SomeObjectToJson(this); } @JsonSerializable(explicitToJson: true) -class SomeQuery$Query with EquatableMixin { - SomeQuery$Query(); +class SomeQuery$Response with EquatableMixin { + SomeQuery$Response(); - factory SomeQuery$Query.fromJson(Map json) => - _$SomeQuery$QueryFromJson(json); + factory SomeQuery$Response.fromJson(Map json) => + _$SomeQuery$ResponseFromJson(json); String thisIsAString; - SomeQuery$Query$SomeObject o; + SomeQuery$Response$SomeObject o; @override List get props => [thisIsAString, o]; - Map toJson() => _$SomeQuery$QueryToJson(this); + Map toJson() => _$SomeQuery$ResponseToJson(this); } -enum SomeQuery$Query$SomeObject$ThisIsAnEnum { +enum SomeQuery$Response$SomeObject$ThisIsAnEnum { A, B, ARTEMIS_UNKNOWN, diff --git a/test/query_generator/aliases/alias_on_object_test.dart b/test/query_generator/aliases/alias_on_object_test.dart index 84e01a91..e7bd41a8 100644 --- a/test/query_generator/aliases/alias_on_object_test.dart +++ b/test/query_generator/aliases/alias_on_object_test.dart @@ -1,5 +1,5 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; +import 'package:gql/language.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -10,62 +10,50 @@ void main() { 'Objects can be aliased', () async => testGenerator( query: query, + schema: r''' + schema { + query: QueryResponse + } + + type QueryResponse { + s: String + o: SomeObject + ob: [SomeObject] + } + + type SomeObject { + st: String + str: String + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, ), ); }); } const query = r''' - query some_query { - s - o { - st - } - anotherObject: ob { - str - } - } - '''; - -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 's', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'o', - type: - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT)), - GraphQLField( - name: 'ob', - type: GraphQLType( - kind: GraphQLTypeKind.LIST, - ofType: GraphQLType( - name: 'SomeObject', kind: GraphQLTypeKind.OBJECT))), - ]), - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 'st', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'str', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ + query some_query { + s + o { + st + } + anotherObject: ob { + str + } + } +'''; + +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( + document: parseString(query), queryName: r'some_query', - queryType: r'SomeQuery$Query', + queryType: r'SomeQuery$QueryResponse', classes: [ ClassDefinition( - name: r'SomeQuery$Query$SomeObject', + name: r'SomeQuery$QueryResponse$SomeObject', properties: [ ClassProperty( type: r'String', @@ -77,7 +65,7 @@ final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ typeNameField: r'__typename', isInput: false), ClassDefinition( - name: r'SomeQuery$Query$AnotherObject', + name: r'SomeQuery$QueryResponse$AnotherObject', properties: [ ClassProperty( type: r'String', @@ -89,7 +77,7 @@ final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ typeNameField: r'__typename', isInput: false), ClassDefinition( - name: r'SomeQuery$Query', + name: r'SomeQuery$QueryResponse', properties: [ ClassProperty( type: r'String', @@ -97,12 +85,12 @@ final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ isOverride: false, isNonNull: false), ClassProperty( - type: r'SomeQuery$Query$SomeObject', + type: r'SomeQuery$QueryResponse$SomeObject', name: r'o', isOverride: false, isNonNull: false), ClassProperty( - type: r'List', + type: r'List', name: r'anotherObject', isOverride: false, isNonNull: false) @@ -123,48 +111,52 @@ import 'package:gql/ast.dart'; part 'query.g.dart'; @JsonSerializable(explicitToJson: true) -class SomeQuery$Query$SomeObject with EquatableMixin { - SomeQuery$Query$SomeObject(); +class SomeQuery$QueryResponse$SomeObject with EquatableMixin { + SomeQuery$QueryResponse$SomeObject(); - factory SomeQuery$Query$SomeObject.fromJson(Map json) => - _$SomeQuery$Query$SomeObjectFromJson(json); + factory SomeQuery$QueryResponse$SomeObject.fromJson( + Map json) => + _$SomeQuery$QueryResponse$SomeObjectFromJson(json); String st; @override List get props => [st]; - Map toJson() => _$SomeQuery$Query$SomeObjectToJson(this); + Map toJson() => + _$SomeQuery$QueryResponse$SomeObjectToJson(this); } @JsonSerializable(explicitToJson: true) -class SomeQuery$Query$AnotherObject with EquatableMixin { - SomeQuery$Query$AnotherObject(); +class SomeQuery$QueryResponse$AnotherObject with EquatableMixin { + SomeQuery$QueryResponse$AnotherObject(); - factory SomeQuery$Query$AnotherObject.fromJson(Map json) => - _$SomeQuery$Query$AnotherObjectFromJson(json); + factory SomeQuery$QueryResponse$AnotherObject.fromJson( + Map json) => + _$SomeQuery$QueryResponse$AnotherObjectFromJson(json); String str; @override List get props => [str]; - Map toJson() => _$SomeQuery$Query$AnotherObjectToJson(this); + Map toJson() => + _$SomeQuery$QueryResponse$AnotherObjectToJson(this); } @JsonSerializable(explicitToJson: true) -class SomeQuery$Query with EquatableMixin { - SomeQuery$Query(); +class SomeQuery$QueryResponse with EquatableMixin { + SomeQuery$QueryResponse(); - factory SomeQuery$Query.fromJson(Map json) => - _$SomeQuery$QueryFromJson(json); + factory SomeQuery$QueryResponse.fromJson(Map json) => + _$SomeQuery$QueryResponseFromJson(json); String s; - SomeQuery$Query$SomeObject o; + SomeQuery$QueryResponse$SomeObject o; - List anotherObject; + List anotherObject; @override List get props => [s, o, anotherObject]; - Map toJson() => _$SomeQuery$QueryToJson(this); + Map toJson() => _$SomeQuery$QueryResponseToJson(this); } '''; diff --git a/test/query_generator/ast_schema/field_not_found_mutation_test.dart b/test/query_generator/ast_schema/field_not_found_mutation_test.dart new file mode 100644 index 00000000..c547e897 --- /dev/null +++ b/test/query_generator/ast_schema/field_not_found_mutation_test.dart @@ -0,0 +1,205 @@ +import 'package:artemis/generator/data.dart'; +import 'package:test/test.dart'; + +import '../../helpers.dart'; + +void main() { + group('On AST schema', () { + test( + 'Field was not found on mutation', + () async => testGenerator( + query: query, + schema: r''' + schema { + mutation: MutationRoot + } + + input CreateThingInput { + clientId: ID! + message: String + } + + type Thing { + id: ID! + message: String + } + + type CreateThingResponse { + thing: Thing + } + + type MutationRoot { + createThing(input: CreateThingInput): CreateThingResponse + } + ''', + libraryDefinition: libraryDefinition, + generatedFile: generatedFile, + ), + ); + }); +} + +const query = r''' +mutation createThing($createThingInput: CreateThingInput) { + createThing(input: $createThingInput) { + thing { + id + message + } + } +} +'''; + +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ + QueryDefinition( + queryName: r'createThing', + queryType: r'CreateThing$MutationRoot', + classes: [ + ClassDefinition( + name: r'CreateThing$MutationRoot$CreateThingResponse$Thing', + properties: [ + ClassProperty( + type: r'String', + name: r'id', + isOverride: false, + isNonNull: true, + isResolveType: false), + ClassProperty( + type: r'String', + name: r'message', + isOverride: false, + isNonNull: false, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'CreateThing$MutationRoot$CreateThingResponse', + properties: [ + ClassProperty( + type: r'CreateThing$MutationRoot$CreateThingResponse$Thing', + name: r'thing', + isOverride: false, + isNonNull: false, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'CreateThing$MutationRoot', + properties: [ + ClassProperty( + type: r'CreateThing$MutationRoot$CreateThingResponse', + name: r'createThing', + isOverride: false, + isNonNull: false, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'CreateThing$CreateThingInput', + properties: [ + ClassProperty( + type: r'String', + name: r'clientId', + isOverride: false, + isNonNull: true, + isResolveType: false), + ClassProperty( + type: r'String', + name: r'message', + isOverride: false, + isNonNull: false, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: true) + ], + inputs: [ + QueryInput( + type: r'CreateThing$CreateThingInput', + name: r'createThingInput', + isNonNull: false) + ], + generateHelpers: false, + suffix: r'Mutation') +]); + +const generatedFile = r'''// GENERATED CODE - DO NOT MODIFY BY HAND + +import 'package:meta/meta.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:equatable/equatable.dart'; +import 'package:gql/ast.dart'; +part 'query.g.dart'; + +@JsonSerializable(explicitToJson: true) +class CreateThing$MutationRoot$CreateThingResponse$Thing with EquatableMixin { + CreateThing$MutationRoot$CreateThingResponse$Thing(); + + factory CreateThing$MutationRoot$CreateThingResponse$Thing.fromJson( + Map json) => + _$CreateThing$MutationRoot$CreateThingResponse$ThingFromJson(json); + + String id; + + String message; + + @override + List get props => [id, message]; + Map toJson() => + _$CreateThing$MutationRoot$CreateThingResponse$ThingToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class CreateThing$MutationRoot$CreateThingResponse with EquatableMixin { + CreateThing$MutationRoot$CreateThingResponse(); + + factory CreateThing$MutationRoot$CreateThingResponse.fromJson( + Map json) => + _$CreateThing$MutationRoot$CreateThingResponseFromJson(json); + + CreateThing$MutationRoot$CreateThingResponse$Thing thing; + + @override + List get props => [thing]; + Map toJson() => + _$CreateThing$MutationRoot$CreateThingResponseToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class CreateThing$MutationRoot with EquatableMixin { + CreateThing$MutationRoot(); + + factory CreateThing$MutationRoot.fromJson(Map json) => + _$CreateThing$MutationRootFromJson(json); + + CreateThing$MutationRoot$CreateThingResponse createThing; + + @override + List get props => [createThing]; + Map toJson() => _$CreateThing$MutationRootToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class CreateThing$CreateThingInput with EquatableMixin { + CreateThing$CreateThingInput({@required this.clientId, this.message}); + + factory CreateThing$CreateThingInput.fromJson(Map json) => + _$CreateThing$CreateThingInputFromJson(json); + + String clientId; + + String message; + + @override + List get props => [clientId, message]; + Map toJson() => _$CreateThing$CreateThingInputToJson(this); +} +'''; diff --git a/test/query_generator/complex_input_objects_test.dart b/test/query_generator/complex_input_objects_test.dart index ad539aeb..88badedb 100644 --- a/test/query_generator/complex_input_objects_test.dart +++ b/test/query_generator/complex_input_objects_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -9,59 +8,46 @@ void main() { test( 'On complex input objects', () async => testGenerator( - query: - r'query some_query($filter: ComplexType!) { o(filter: $filter) { s } }', + query: r''' + query some_query($filter: ComplexType!) { + o(filter: $filter) { + s + } + }''', + schema: r''' + schema { + query: QueryRoot + } + + type QueryRoot { + o(filter: ComplexType!): SomeObject + } + + type ComplexType { + s: String! + e: MyEnum + ls: [String] + } + + type SomeObject { + s: String + } + + enum MyEnum { + value1 + value2 + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, generateHelpers: true, ), ); }); } -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'QueryRoot', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM, enumValues: [ - GraphQLEnumValue(name: 'value1'), - GraphQLEnumValue(name: 'value2'), - ]), - GraphQLType( - name: 'ComplexType', - kind: GraphQLTypeKind.INPUT_OBJECT, - inputFields: [ - GraphQLInputValue( - name: 's', - type: GraphQLType( - kind: GraphQLTypeKind.NON_NULL, - ofType: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR))), - GraphQLInputValue( - name: 'e', - type: GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM)), - GraphQLInputValue( - name: 'ls', - type: GraphQLType( - kind: GraphQLTypeKind.LIST, - ofType: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR))), - ]), - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 's', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - ]), - GraphQLType(name: 'QueryRoot', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 'o', - type: GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT)), - ]), - ], -); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'some_query', queryType: r'SomeQuery$QueryRoot', diff --git a/test/query_generator/enums/enum_list_test.dart b/test/query_generator/enums/enum_list_test.dart index 32ed846a..e2697d4c 100644 --- a/test/query_generator/enums/enum_list_test.dart +++ b/test/query_generator/enums/enum_list_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -12,7 +11,23 @@ void main() { query: query, libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, + schema: r''' + schema { + query: QueryRoot + } + + type QueryRoot { + q: QueryResponse + } + + type QueryResponse { + le: [MyEnum] + } + + enum MyEnum { + A + B + }''', ), ); }); @@ -26,38 +41,8 @@ query custom { } '''; -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'QueryRoot', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM, enumValues: [ - GraphQLEnumValue(name: 'A'), - GraphQLEnumValue(name: 'B'), - ]), - GraphQLType( - name: 'QueryResponse', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'le', - type: GraphQLType( - kind: GraphQLTypeKind.LIST, - ofType: - GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM))), - ], - ), - GraphQLType( - name: 'QueryRoot', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'q', - type: GraphQLType( - name: 'QueryResponse', kind: GraphQLTypeKind.OBJECT)), - ], - ), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'custom', queryType: r'Custom$QueryRoot', diff --git a/test/query_generator/enums/input_enum_test.dart b/test/query_generator/enums/input_enum_test.dart index b1e96141..3da23cf0 100644 --- a/test/query_generator/enums/input_enum_test.dart +++ b/test/query_generator/enums/input_enum_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -10,9 +9,35 @@ void main() { 'Enums can be part of input objects', () async => testGenerator( query: query, + schema: r''' + schema { + query: QueryRoot + } + + type QueryRoot { + q(input: Input!, o: OtherEnum!): QueryResponse + } + + type QueryResponse { + s: String + } + + type Input { + e: MyEnum! + } + + enum MyEnum { + A + B + } + + enum OtherEnum { + O1 + O2 + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, generateHelpers: true, ), ); @@ -20,75 +45,15 @@ void main() { } const query = r''' -query custom($input: Input!, $o: OtherEnum!) { - q(input: $input, o: $o) { - s + query custom($input: Input!, $o: OtherEnum!) { + q(input: $input, o: $o) { + s + } } -} '''; -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'QueryRoot', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'Input', - kind: GraphQLTypeKind.INPUT_OBJECT, - inputFields: [ - GraphQLInputValue( - name: 'e', - type: GraphQLType( - kind: GraphQLTypeKind.NON_NULL, - ofType: - GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM))), - ], - ), - GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM, enumValues: [ - GraphQLEnumValue(name: 'A'), - GraphQLEnumValue(name: 'B'), - ]), - GraphQLType(name: 'OtherEnum', kind: GraphQLTypeKind.ENUM, enumValues: [ - GraphQLEnumValue(name: 'O1'), - GraphQLEnumValue(name: 'O2'), - ]), - GraphQLType( - name: 'QueryResponse', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 's', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - ], - ), - GraphQLType( - name: 'QueryRoot', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'q', - args: [ - GraphQLInputValue( - name: 'input', - type: GraphQLType( - kind: GraphQLTypeKind.NON_NULL, - ofType: GraphQLType( - name: 'Input', kind: GraphQLTypeKind.INPUT_OBJECT)), - ), - GraphQLInputValue( - name: 'o', - type: GraphQLType( - kind: GraphQLTypeKind.NON_NULL, - ofType: GraphQLType( - name: 'OtherEnum', kind: GraphQLTypeKind.ENUM)), - ), - ], - type: GraphQLType( - name: 'QueryResponse', kind: GraphQLTypeKind.OBJECT)), - ], - ), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'custom', queryType: r'Custom$QueryRoot', diff --git a/test/query_generator/enums/query_enum_test.dart b/test/query_generator/enums/query_enum_test.dart index 3dd02a3d..fa9adaea 100644 --- a/test/query_generator/enums/query_enum_test.dart +++ b/test/query_generator/enums/query_enum_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -10,51 +9,41 @@ void main() { 'Enums can be part of queries responses', () async => testGenerator( query: query, + schema: r''' + schema { + query: QueryRoot + } + + type QueryRoot { + q: QueryResponse + } + + type QueryResponse { + e: MyEnum + } + + enum MyEnum { + A + B + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, ), ); }); } const query = r''' -query custom { - q { - e + query custom { + q { + e + } } -} '''; -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'QueryRoot', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM, enumValues: [ - GraphQLEnumValue(name: 'A'), - GraphQLEnumValue(name: 'B'), - ]), - GraphQLType( - name: 'QueryResponse', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'e', - type: GraphQLType(name: 'MyEnum', kind: GraphQLTypeKind.ENUM)), - ], - ), - GraphQLType( - name: 'QueryRoot', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'q', - type: GraphQLType( - name: 'QueryResponse', kind: GraphQLTypeKind.OBJECT)), - ], - ), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'custom', queryType: r'Custom$QueryRoot', diff --git a/test/query_generator/errors/generation_errors_test.dart b/test/query_generator/errors/generation_errors_test.dart index 2e1d2911..465c6075 100644 --- a/test/query_generator/errors/generation_errors_test.dart +++ b/test/query_generator/errors/generation_errors_test.dart @@ -1,6 +1,4 @@ import 'package:artemis/builder.dart'; -import 'package:artemis/generator/graphql_helpers.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:build/build.dart'; import 'package:build_test/build_test.dart'; import 'package:test/test.dart'; @@ -10,12 +8,11 @@ import '../../helpers.dart'; void main() { group('On errors', () { test('When the schema file is not found', () async { - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({ + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'non_existent_api.schema.json', + 'schema': 'non_existent_api.schema.graphql', 'queries_glob': '**.graphql', 'output': 'lib/some_query.dart', }, @@ -29,21 +26,22 @@ void main() { anotherBuilder, { 'a|api.schema.json': '', + 'a|api.schema.grqphql': '', 'a|some_query.query.graphql': 'query some_query { s }', }, onLog: debug), throwsA(predicate((e) => e is Exception))); }); - test('When user hasn\'t configured an output', () async { + test("When user hasn't configured an output", () async { expect( () { graphQLQueryBuilder(BuilderOptions({ 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.grqphql', + 'queries_glob': 'queries/**.graphql', }, ], })); @@ -53,12 +51,11 @@ void main() { }); test('When queries_glob is not configured', () async { - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({ + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', + 'schema': 'api.schema.graphql', 'output': 'lib/some_query.dart', }, ], @@ -66,27 +63,10 @@ void main() { anotherBuilder.onBuild = expectAsync1((_) => null, count: 0); - final GraphQLSchema schema = GraphQLSchema( - queryType: - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 's', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); - expect( () => testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), 'a|some_query.query.graphql': 'query some_query { s }', }, onLog: debug), diff --git a/test/query_generator/fragments/fragment_glob_test.dart b/test/query_generator/fragments/fragment_glob_test.dart index ca1b00a1..a8ca6561 100644 --- a/test/query_generator/fragments/fragment_glob_test.dart +++ b/test/query_generator/fragments/fragment_glob_test.dart @@ -9,9 +9,36 @@ void main() { 'Extracting', () async => testGenerator( query: queryString, + schema: r''' + schema { + query: Query + } + + type Query { + pokemon(id: String, name: String): Pokemon + } + + type Pokemon { + id: String! + name: String + evolutions: [Pokemon] + attacks: PokemonAttack + weight: PokemonDimension + } + + type PokemonAttack { + special: [Attack] + } + + type PokemonDimension { + minimum: String + } + type Attack { + name: String + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - stringSchema: pokemonSchema, builderOptionsMap: {'fragments_glob': '**.frag'}, sourceAssetsMap: {'a|fragment.frag': fragmentsString}, generateHelpers: true, @@ -21,33 +48,44 @@ void main() { } const fragmentsString = ''' - fragment Pokemon on Pokemon { - id - weight { - ...weight - } - attacks { - ...pokemonAttack - } - } - fragment weight on PokemonDimension { minimum } - fragment pokemonAttack on PokemonAttack { - special { ...attack } - } - fragment attack on Attack { name } - '''; + fragment Pokemon on Pokemon { + id + weight { + ...weight + } + attacks { + ...pokemonAttack + } + } + + fragment weight on PokemonDimension { + minimum + } + + fragment pokemonAttack on PokemonAttack { + special { + ...attack + } + } + + fragment attack on Attack { + name + } +'''; const queryString = ''' - { - pokemon(name: "Pikachu") { - ...Pokemon - evolutions { - ...Pokemon - } - } - }'''; + { + pokemon(name: "Pikachu") { + ...Pokemon + evolutions { + ...Pokemon + } + } + } +'''; -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'query', queryType: r'Query$Query', @@ -370,143 +408,3 @@ class QueryQuery extends GraphQLQuery { Query$Query parse(Map json) => Query$Query.fromJson(json); } '''; - -const pokemonSchema = ''' -{ - "data": { - "__schema": { - "queryType": { - "name": "Query" - }, - "types": [ - { - "kind": "OBJECT", - "name": "Query", - "fields": [ - { - "name": "query", - "type": { - "kind": "OBJECT", - "name": "Query" - } - }, - { - "name": "pokemon", - "args": [ - { - "name": "id", - "type": { - "kind": "SCALAR", - "name": "String" - } - }, - { - "name": "name", - "type": { - "kind": "SCALAR", - "name": "String" - } - } - ], - "type": { - "kind": "OBJECT", - "name": "Pokemon" - } - } - ] - }, - { - "kind": "OBJECT", - "name": "Pokemon", - "fields": [ - { - "name": "id", - "type": { - "kind": "NON_NULL", - "ofType": { - "kind": "SCALAR", - "name": "ID" - } - } - }, - { - "name": "weight", - "type": { - "kind": "OBJECT", - "name": "PokemonDimension" - } - }, - { - "name": "attacks", - "type": { - "kind": "OBJECT", - "name": "PokemonAttack" - } - }, - { - "name": "evolutions", - "type": { - "kind": "LIST", - "ofType": { - "kind": "OBJECT", - "name": "Pokemon" - } - } - } - ] - }, - { - "kind": "SCALAR", - "name": "ID" - }, - { - "kind": "OBJECT", - "name": "PokemonDimension", - "fields": [ - { - "name": "minimum", - "type": { - "kind": "SCALAR", - "name": "String" - } - } - ] - }, - { - "kind": "OBJECT", - "name": "PokemonAttack", - "fields": [ - { - "name": "special", - "type": { - "kind": "LIST", - "ofType": { - "kind": "OBJECT", - "name": "Attack" - } - } - } - ] - }, - { - "kind": "SCALAR", - "name": "String" - }, - { - "kind": "OBJECT", - "name": "Attack", - "fields": [ - { - "name": "name", - "type": { - "kind": "SCALAR", - "name": "String" - } - } - ] - } - ] - } - } -} -'''; diff --git a/test/query_generator/fragments/fragment_on_fragments.dart b/test/query_generator/fragments/fragment_on_fragments_test.dart similarity index 59% rename from test/query_generator/fragments/fragment_on_fragments.dart rename to test/query_generator/fragments/fragment_on_fragments_test.dart index ebaf7fd2..ff33dceb 100644 --- a/test/query_generator/fragments/fragment_on_fragments.dart +++ b/test/query_generator/fragments/fragment_on_fragments_test.dart @@ -9,9 +9,23 @@ void main() { 'Properties will be merged', () async => testGenerator( query: queryString, + schema: r''' + schema { + query: Query + } + + type Query { + pokemon(id: String, name: String): Pokemon + } + + type Pokemon { + id: String! + name: String + number: String + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - stringSchema: pokemonSchema, builderOptionsMap: {'fragments_glob': '**.frag'}, sourceAssetsMap: {'a|fragment.frag': fragmentsString}, ), @@ -20,24 +34,27 @@ void main() { } const fragmentsString = ''' - fragment PokemonParts on Pokemon { - number - name - } - fragment Pokemon on Pokemon { - id - ...PokemonParts - } - '''; + fragment PokemonParts on Pokemon { + number + name + } + + fragment Pokemon on Pokemon { + id + ...PokemonParts + } +'''; const queryString = ''' - { - pokemon(name: "Pikachu") { - ...Pokemon - } - }'''; + { + pokemon(name: "Pikachu") { + ...Pokemon + } + } +'''; -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'query', queryType: r'Query$Query', @@ -129,91 +146,3 @@ class Query$Query with EquatableMixin { Map toJson() => _$Query$QueryToJson(this); } '''; - -const pokemonSchema = ''' -{ - "data": { - "__schema": { - "queryType": { - "name": "Query" - }, - "types": [ - { - "kind": "OBJECT", - "name": "Query", - "fields": [ - { - "name": "query", - "type": { - "kind": "OBJECT", - "name": "Query" - } - }, - { - "name": "pokemon", - "args": [ - { - "name": "id", - "type": { - "kind": "SCALAR", - "name": "String" - } - }, - { - "name": "name", - "type": { - "kind": "SCALAR", - "name": "String" - } - } - ], - "type": { - "kind": "OBJECT", - "name": "Pokemon" - } - } - ] - }, - { - "kind": "OBJECT", - "name": "Pokemon", - "fields": [ - { - "name": "id", - "type": { - "kind": "NON_NULL", - "ofType": { - "kind": "SCALAR", - "name": "ID" - } - } - }, - { - "name": "number", - "type": { - "kind": "SCALAR", - "name": "String" - } - }, - { - "name": "name", - "type": { - "kind": "SCALAR", - "name": "String" - } - } - ] - }, - { - "kind": "SCALAR", - "name": "ID" - }, - { - "kind": "SCALAR", - "name": "String" - } - ] - } - } -} -'''; diff --git a/test/query_generator/fragments/fragments_multiple_test.dart b/test/query_generator/fragments/fragments_multiple_test.dart new file mode 100644 index 00000000..48224da1 --- /dev/null +++ b/test/query_generator/fragments/fragments_multiple_test.dart @@ -0,0 +1,454 @@ +import 'package:artemis/generator/data.dart'; +import 'package:test/test.dart'; + +import '../../helpers.dart'; + +void main() { + group('On fragments multiple', () { + test( + 'Fragments will have their own classes multiple', + () async => testGenerator( + query: r''' + fragment Dst on Destination { + id + name + } + + fragment Departure on Destination { + id + } + + query VoyagesData($input: PaginationInput!) { + voyages(pagination: $input) { + voyages { + numberOfReports + voyage { + dateFrom + dateTo + id + voyageNumber + } + } + } + } + ''', + schema: r''' + schema { + query: Query + } + + type Query { + voyages(pagination: PaginationInput!): VoyageList! + } + + type VoyageList { + voyages: [VoyageDetails!]! + } + + type VoyageDetails { + numberOfReports: Int! + voyage: Voyage! + } + + type Voyage { + arrival: Destination! + dateFrom: DateTime! + dateTo: DateTime + departure: Destination! + visitPoint: Destination! + id: ID + voyageNumber: String! + } + + type Destination { + id: ID! + name: String! + } + + input PaginationInput { + limit: Int! + offset: Int! + } + ''', + libraryDefinition: libraryDefinition, + generatedFile: generatedFile, + generateHelpers: true), + ); + }); +} + +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ + QueryDefinition( + queryName: r'VoyagesData', + queryType: r'VoyagesData$Query', + classes: [ + FragmentClassDefinition(name: r'VoyagesData$DstMixin', properties: [ + ClassProperty( + type: r'String', + name: r'id', + isOverride: false, + isNonNull: true, + isResolveType: false), + ClassProperty( + type: r'String', + name: r'name', + isOverride: false, + isNonNull: true, + isResolveType: false) + ]), + FragmentClassDefinition( + name: r'VoyagesData$DepartureMixin', + properties: [ + ClassProperty( + type: r'String', + name: r'id', + isOverride: false, + isNonNull: true, + isResolveType: false) + ]), + ClassDefinition( + name: r'VoyagesData$Query$VoyageList$VoyageDetails$Voyage', + properties: [ + ClassProperty( + type: r'DateTime', + name: r'dateFrom', + isOverride: false, + isNonNull: true, + isResolveType: false), + ClassProperty( + type: r'DateTime', + name: r'dateTo', + isOverride: false, + isNonNull: false, + isResolveType: false), + ClassProperty( + type: r'String', + name: r'id', + isOverride: false, + isNonNull: false, + isResolveType: false), + ClassProperty( + type: r'String', + name: r'voyageNumber', + isOverride: false, + isNonNull: true, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'VoyagesData$Query$VoyageList$VoyageDetails', + properties: [ + ClassProperty( + type: r'int', + name: r'numberOfReports', + isOverride: false, + isNonNull: true, + isResolveType: false), + ClassProperty( + type: r'VoyagesData$Query$VoyageList$VoyageDetails$Voyage', + name: r'voyage', + isOverride: false, + isNonNull: true, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'VoyagesData$Query$VoyageList', + properties: [ + ClassProperty( + type: r'List', + name: r'voyages', + isOverride: false, + isNonNull: true, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'VoyagesData$Query', + properties: [ + ClassProperty( + type: r'VoyagesData$Query$VoyageList', + name: r'voyages', + isOverride: false, + isNonNull: true, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'VoyagesData$PaginationInput', + properties: [ + ClassProperty( + type: r'int', + name: r'limit', + isOverride: false, + isNonNull: true, + isResolveType: false), + ClassProperty( + type: r'int', + name: r'offset', + isOverride: false, + isNonNull: true, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: true) + ], + inputs: [ + QueryInput( + type: r'VoyagesData$PaginationInput', + name: r'input', + isNonNull: true) + ], + generateHelpers: true, + suffix: r'Query') +]); + +const generatedFile = r'''// GENERATED CODE - DO NOT MODIFY BY HAND + +import 'package:meta/meta.dart'; +import 'package:artemis/artemis.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:equatable/equatable.dart'; +import 'package:gql/ast.dart'; +part 'query.g.dart'; + +mixin VoyagesData$DstMixin { + String id; + String name; +} +mixin VoyagesData$DepartureMixin { + String id; +} + +@JsonSerializable(explicitToJson: true) +class VoyagesData$Query$VoyageList$VoyageDetails$Voyage with EquatableMixin { + VoyagesData$Query$VoyageList$VoyageDetails$Voyage(); + + factory VoyagesData$Query$VoyageList$VoyageDetails$Voyage.fromJson( + Map json) => + _$VoyagesData$Query$VoyageList$VoyageDetails$VoyageFromJson(json); + + DateTime dateFrom; + + DateTime dateTo; + + String id; + + String voyageNumber; + + @override + List get props => [dateFrom, dateTo, id, voyageNumber]; + Map toJson() => + _$VoyagesData$Query$VoyageList$VoyageDetails$VoyageToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class VoyagesData$Query$VoyageList$VoyageDetails with EquatableMixin { + VoyagesData$Query$VoyageList$VoyageDetails(); + + factory VoyagesData$Query$VoyageList$VoyageDetails.fromJson( + Map json) => + _$VoyagesData$Query$VoyageList$VoyageDetailsFromJson(json); + + int numberOfReports; + + VoyagesData$Query$VoyageList$VoyageDetails$Voyage voyage; + + @override + List get props => [numberOfReports, voyage]; + Map toJson() => + _$VoyagesData$Query$VoyageList$VoyageDetailsToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class VoyagesData$Query$VoyageList with EquatableMixin { + VoyagesData$Query$VoyageList(); + + factory VoyagesData$Query$VoyageList.fromJson(Map json) => + _$VoyagesData$Query$VoyageListFromJson(json); + + List voyages; + + @override + List get props => [voyages]; + Map toJson() => _$VoyagesData$Query$VoyageListToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class VoyagesData$Query with EquatableMixin { + VoyagesData$Query(); + + factory VoyagesData$Query.fromJson(Map json) => + _$VoyagesData$QueryFromJson(json); + + VoyagesData$Query$VoyageList voyages; + + @override + List get props => [voyages]; + Map toJson() => _$VoyagesData$QueryToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class VoyagesData$PaginationInput with EquatableMixin { + VoyagesData$PaginationInput({@required this.limit, @required this.offset}); + + factory VoyagesData$PaginationInput.fromJson(Map json) => + _$VoyagesData$PaginationInputFromJson(json); + + int limit; + + int offset; + + @override + List get props => [limit, offset]; + Map toJson() => _$VoyagesData$PaginationInputToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class VoyagesDataArguments extends JsonSerializable with EquatableMixin { + VoyagesDataArguments({@required this.input}); + + factory VoyagesDataArguments.fromJson(Map json) => + _$VoyagesDataArgumentsFromJson(json); + + final VoyagesData$PaginationInput input; + + @override + List get props => [input]; + Map toJson() => _$VoyagesDataArgumentsToJson(this); +} + +class VoyagesDataQuery + extends GraphQLQuery { + VoyagesDataQuery({this.variables}); + + @override + final DocumentNode document = DocumentNode(definitions: [ + FragmentDefinitionNode( + name: NameNode(value: 'Dst'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'Destination'), isNonNull: false)), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null) + ])), + FragmentDefinitionNode( + name: NameNode(value: 'Departure'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'Destination'), isNonNull: false)), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null) + ])), + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'VoyagesData'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'input')), + type: NamedTypeNode( + name: NameNode(value: 'PaginationInput'), isNonNull: true), + defaultValue: DefaultValueNode(value: null), + directives: []) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'voyages'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'pagination'), + value: VariableNode(name: NameNode(value: 'input'))) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'voyages'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'numberOfReports'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'voyage'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'dateFrom'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'dateTo'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'voyageNumber'), + alias: null, + arguments: [], + directives: [], + selectionSet: null) + ])) + ])) + ])) + ])) + ]); + + @override + final String operationName = 'VoyagesData'; + + @override + final VoyagesDataArguments variables; + + @override + List get props => [document, operationName, variables]; + @override + VoyagesData$Query parse(Map json) => + VoyagesData$Query.fromJson(json); +} +'''; diff --git a/test/query_generator/fragments/fragments_test.dart b/test/query_generator/fragments/fragments_test.dart index a7de9d76..b2135190 100644 --- a/test/query_generator/fragments/fragments_test.dart +++ b/test/query_generator/fragments/fragments_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -9,32 +8,34 @@ void main() { test( 'Fragments will have their own classes', () async => testGenerator( - query: - 'fragment myFragment on SomeObject { s, i }\nquery some_query { ...myFragment }', + query: r''' + fragment myFragment on SomeObject { + s, i + } + + query some_query { + ...myFragment + } + ''', + schema: r''' + schema { + query: SomeObject + } + + type SomeObject { + s: String + i: Int + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, ), ); }); } -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 's', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'i', - type: GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'some_query', queryType: r'SomeQuery$SomeObject', diff --git a/test/query_generator/interfaces/interface_possible_types_test.dart b/test/query_generator/interfaces/interface_possible_types_test.dart index 9cade42b..06bd9d70 100644 --- a/test/query_generator/interfaces/interface_possible_types_test.dart +++ b/test/query_generator/interfaces/interface_possible_types_test.dart @@ -9,342 +9,64 @@ void main() { 'Those other types are not considered nor generated', () async => testGenerator( query: query, + schema: graphQLSchema, libraryDefinition: libraryDefinition, generatedFile: generatedFile, - stringSchema: stringSchema, ), ); }); } const query = r''' -query custom($id: ID!) { - nodeById(id: $id) { - id - ... on User { - username - } - ... on ChatMessage { - message + query custom($id: ID!) { + nodeById(id: $id) { + id + ... on User { + username + } + ... on ChatMessage { + message + } } } -} '''; // https://graphql-code-generator.com/#live-demo -final graphQLSchema = ''' -scalar String -scalar ID - -schema { - query: Query -} - -type Query { - nodeById(id: ID!): Node -} - -interface Node { - id: ID! -} - -type User implements Node { - id: ID! - username: String! -} - -type OtherEntity implements Node { - id: ID! - test: String! -} - -type ChatMessage implements Node { - id: ID! - message: String! - user: User! -} -'''; - -const stringSchema = r''' -{ - "__schema": { - "queryType": { - "name": "Query" - }, - "mutationType": null, - "subscriptionType": null, - "types": [ - { - "kind": "OBJECT", - "name": "Query", - "description": null, - "fields": [ - { - "name": "nodeById", - "description": null, - "args": [ - { - "name": "id", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INTERFACE", - "name": "Node", - "description": null, - "fields": [ - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "ChatMessage", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "OtherEntity", - "ofType": null - } - ] - }, - { - "kind": "OBJECT", - "name": "User", - "description": null, - "fields": [ - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "username", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "OtherEntity", - "description": null, - "fields": [ - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "test", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ChatMessage", - "description": null, - "fields": [ - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "message", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "User", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "String", - "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ID", - "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - } - ], - "directives": [] - } -} +const graphQLSchema = ''' + scalar String + scalar ID + + schema { + query: Query + } + + type Query { + nodeById(id: ID!): Node + } + + interface Node { + id: ID! + } + + type User implements Node { + id: ID! + username: String! + } + + type OtherEntity implements Node { + id: ID! + test: String! + } + + type ChatMessage implements Node { + id: ID! + message: String! + user: User! + } '''; -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'custom', queryType: r'Custom$Query', diff --git a/test/query_generator/interfaces/interface_test.dart b/test/query_generator/interfaces/interface_test.dart index 95604a14..f4d1c24d 100644 --- a/test/query_generator/interfaces/interface_test.dart +++ b/test/query_generator/interfaces/interface_test.dart @@ -9,291 +9,67 @@ void main() { 'On interfaces', () async => testGenerator( query: query, + schema: graphQLSchema, libraryDefinition: libraryDefinition, generatedFile: generatedFile, - stringSchema: stringSchema, ), ); }); } const query = r''' -query custom($id: ID!) { - nodeById(id: $id) { - id - ... on User { - ...UserFrag - } - ... on ChatMessage { - message - user { + query custom($id: ID!) { + nodeById(id: $id) { + id + ... on User { ...UserFrag } + ... on ChatMessage { + message + user { + ...UserFrag + } + } } } -} - -fragment UserFrag on User { - id - username -} + + fragment UserFrag on User { + id + username + } '''; // https://graphql-code-generator.com/#live-demo -final graphQLSchema = ''' -scalar String -scalar ID - -schema { - query: Query -} - -type Query { - nodeById(id: ID!): Node -} - -interface Node { - id: ID! -} - -type User implements Node { - id: ID! - username: String! -} - -type ChatMessage implements Node { - id: ID! - message: String! - user: User! -} -'''; - -const stringSchema = r''' -{ - "__schema": { - "queryType": { - "name": "Query" - }, - "mutationType": null, - "subscriptionType": null, - "types": [ - { - "kind": "OBJECT", - "name": "Query", - "description": null, - "fields": [ - { - "name": "nodeById", - "description": null, - "args": [ - { - "name": "id", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INTERFACE", - "name": "Node", - "description": null, - "fields": [ - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "ChatMessage", - "ofType": null - } - ] - }, - { - "kind": "OBJECT", - "name": "User", - "description": null, - "fields": [ - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "username", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ChatMessage", - "description": null, - "fields": [ - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "message", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "User", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "String", - "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ID", - "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - } - ], - "directives": [] - } -} +final String graphQLSchema = r''' + scalar String + scalar ID + + schema { + query: Query + } + + type Query { + nodeById(id: ID!): Node + } + + interface Node { + id: ID! + } + + type User implements Node { + id: ID! + username: String! + } + + type ChatMessage implements Node { + id: ID! + message: String! + user: User! + } '''; -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'custom', queryType: r'Custom$Query', diff --git a/test/query_generator/multiple_queries_test.dart b/test/query_generator/multiple_queries_test.dart index e719140a..a74c51fe 100644 --- a/test/query_generator/multiple_queries_test.dart +++ b/test/query_generator/multiple_queries_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -10,33 +9,28 @@ void main() { 'Header and part should only be included once', () async => testGenerator( query: r'query some_query { s, i }', + schema: r''' + schema { + query: SomeObject + } + + type SomeObject { + s: String + i: Int + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, sourceAssetsMap: { - 'a|another_query.graphql': 'query another_query { s }', + 'a|queries/another_query.graphql': 'query another_query { s }', }, ), ); }); } -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 's', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'i', - type: GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'some_query', queryType: r'SomeQuery$SomeObject', diff --git a/test/query_generator/mutations_test.dart b/test/query_generator/mutations_test.dart index 84061118..9036b9a5 100644 --- a/test/query_generator/mutations_test.dart +++ b/test/query_generator/mutations_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -10,9 +9,25 @@ void main() { 'The mutation class will be suffixed as Mutation', () async => testGenerator( query: query, + schema: r''' + schema { + mutation: MutationRoot + } + + type MutationRoot { + mut(input: Input!): MutationResponse + } + + type MutationResponse { + s: String + } + + type Input { + s: String! + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, generateHelpers: true, ), ); @@ -27,54 +42,8 @@ mutation custom($input: Input!) { } '''; -final schema = GraphQLSchema( - mutationType: - GraphQLType(name: 'MutationRoot', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'MutationResponse', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 's', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR)), - ], - ), - GraphQLType( - name: 'MutationRoot', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'mut', - args: [ - GraphQLInputValue( - name: 'input', - type: GraphQLType( - kind: GraphQLTypeKind.NON_NULL, - ofType: GraphQLType( - name: 'Input', kind: GraphQLTypeKind.INPUT_OBJECT)), - ), - ], - type: GraphQLType( - name: 'MutationResponse', kind: GraphQLTypeKind.OBJECT)), - ], - ), - GraphQLType( - name: 'Input', - kind: GraphQLTypeKind.INPUT_OBJECT, - inputFields: [ - GraphQLInputValue( - name: 's', - type: GraphQLType( - kind: GraphQLTypeKind.NON_NULL, - ofType: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR))), - ], - ), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'custom', queryType: r'Custom$MutationRoot', diff --git a/test/query_generator/query_generator_test.dart b/test/query_generator/query_generator_test.dart index c2ca8725..4bf6a378 100644 --- a/test/query_generator/query_generator_test.dart +++ b/test/query_generator/query_generator_test.dart @@ -1,8 +1,8 @@ import 'package:artemis/builder.dart'; import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:build/build.dart'; import 'package:build_test/build_test.dart'; +import 'package:gql/language.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -10,35 +10,12 @@ import '../helpers.dart'; void main() { group('On query generation', () { test('When not configured, nothing will be generated', () async { - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({})); - final GraphQLSchema schema = GraphQLSchema( - queryType: - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 's', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'i', - type: GraphQLType( - name: 'Int', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); - - anotherBuilder.onBuild = expectAsync1((_) {}, count: 0); + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({})); await testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), + 'a|api.schema.graphql': '', 'a|some_query.query.graphql': 'query some_query { s, i }', }, onLog: debug, @@ -46,37 +23,16 @@ void main() { }); test('A simple query yields simple classes', () async { - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({ + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.graphql', + 'queries_glob': 'queries/**.graphql', 'output': 'lib/some_query.dart', } ] })); - final GraphQLSchema schema = GraphQLSchema( - queryType: - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 's', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'i', - type: GraphQLType( - name: 'Int', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); anotherBuilder.onBuild = expectAsync1((definition) { final libraryDefinition = @@ -102,8 +58,17 @@ void main() { await testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), - 'a|some_query.query.graphql': 'query some_query { s, i }', + 'a|api.schema.graphql': r''' + schema { + query: SomeObject + } + + type SomeObject { + s: String + i: Int + } + ''', + 'a|queries/some_query.graphql': 'query some_query { s, i }', }, outputs: { 'a|lib/some_query.dart': r'''// GENERATED CODE - DO NOT MODIFY BY HAND @@ -135,104 +100,82 @@ class SomeQuery$SomeObject with EquatableMixin { }); test('A simple query with list input', () async { - var query = r''' - query some_query($intsNonNullable: [Int]!, $stringNullable: String) { - s, - i, - list(intsNonNullable: $intsNonNullable) - } - '''; - - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({ + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.grqphql', + 'queries_glob': 'queries/**.graphql', 'output': 'lib/some_query.dart', } ] })); - final GraphQLSchema schema = GraphQLSchema( - queryType: - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'List', - kind: GraphQLTypeKind.LIST, - ofType: GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR)), - GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 's', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'i', - type: GraphQLType( - name: 'Int', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'list', - type: GraphQLType( - name: 'List', - kind: GraphQLTypeKind.LIST, - ofType: GraphQLType( - name: 'Int', kind: GraphQLTypeKind.SCALAR)), - args: [ - GraphQLInputValue( - name: 'intsNonNullable', - defaultValue: null, - type: GraphQLType( - kind: GraphQLTypeKind.NON_NULL, - ofType: GraphQLType( - name: 'List', - kind: GraphQLTypeKind.LIST, - ofType: GraphQLType( - name: 'Int', - kind: GraphQLTypeKind.SCALAR))), - ), - GraphQLInputValue( - name: 'stringNullable', - defaultValue: null, - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR), - ) - ]), - ]), - ]); + var query = r''' + query some_query($intsNonNullable: [Int]!, $stringNullable: String) { + someQuery(intsNonNullable: $intsNonNullable, stringNullable: $stringNullable) { + s + i + list(intsNonNullable: $intsNonNullable) + } + } + '''; anotherBuilder.onBuild = expectAsync1((definition) { final libraryDefinition = LibraryDefinition(basename: r'some_query', queries: [ QueryDefinition( queryName: r'some_query', - queryType: r'SomeQuery$SomeObject', + queryType: r'SomeQuery$Query', classes: [ ClassDefinition( - name: r'SomeQuery$SomeObject', + name: r'SomeQuery$Query$SomeObject', properties: [ ClassProperty( - type: r'String', name: r's', isOverride: false), + type: r'String', + name: r's', + isOverride: false, + isNonNull: false, + isResolveType: false), ClassProperty( - type: r'int', name: r'i', isOverride: false), + type: r'int', + name: r'i', + isOverride: false, + isNonNull: false, + isResolveType: false), ClassProperty( - type: r'List', name: r'list', isOverride: false) + type: r'List', + name: r'list', + isOverride: false, + isNonNull: true, + isResolveType: false) ], - typeNameField: r'__typename') + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false), + ClassDefinition( + name: r'SomeQuery$Query', + properties: [ + ClassProperty( + type: r'SomeQuery$Query$SomeObject', + name: r'someQuery', + isOverride: false, + isNonNull: false, + isResolveType: false) + ], + factoryPossibilities: {}, + typeNameField: r'__typename', + isInput: false) ], inputs: [ QueryInput( type: r'List', name: r'intsNonNullable', isNonNull: true), - QueryInput(type: r'String', name: r'stringNullable') + QueryInput( + type: r'String', name: r'stringNullable', isNonNull: false) ], - generateHelpers: true) + generateHelpers: true, + suffix: r'Query') ]); expect(definition, libraryDefinition); }, count: 1); @@ -240,8 +183,22 @@ class SomeQuery$SomeObject with EquatableMixin { await testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), - 'a|some_query.query.graphql': query, + 'a|api.schema.grqphql': r''' + schema { + query: Query + } + + type Query { + someQuery(intsNonNullable: [Int]!, stringNullable: String): SomeObject + } + + type SomeObject { + s: String + i: Int + list(intsNonNullable: [Int]!): [Int]! + } + ''', + 'a|queries/some_query.graphql': query }, outputs: { 'a|lib/some_query.dart': r'''// GENERATED CODE - DO NOT MODIFY BY HAND @@ -254,11 +211,11 @@ import 'package:gql/ast.dart'; part 'some_query.g.dart'; @JsonSerializable(explicitToJson: true) -class SomeQuery$SomeObject with EquatableMixin { - SomeQuery$SomeObject(); +class SomeQuery$Query$SomeObject with EquatableMixin { + SomeQuery$Query$SomeObject(); - factory SomeQuery$SomeObject.fromJson(Map json) => - _$SomeQuery$SomeObjectFromJson(json); + factory SomeQuery$Query$SomeObject.fromJson(Map json) => + _$SomeQuery$Query$SomeObjectFromJson(json); String s; @@ -268,7 +225,21 @@ class SomeQuery$SomeObject with EquatableMixin { @override List get props => [s, i, list]; - Map toJson() => _$SomeQuery$SomeObjectToJson(this); + Map toJson() => _$SomeQuery$Query$SomeObjectToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class SomeQuery$Query with EquatableMixin { + SomeQuery$Query(); + + factory SomeQuery$Query.fromJson(Map json) => + _$SomeQuery$QueryFromJson(json); + + SomeQuery$Query$SomeObject someQuery; + + @override + List get props => [someQuery]; + Map toJson() => _$SomeQuery$QueryToJson(this); } @JsonSerializable(explicitToJson: true) @@ -287,8 +258,7 @@ class SomeQueryArguments extends JsonSerializable with EquatableMixin { Map toJson() => _$SomeQueryArgumentsToJson(this); } -class SomeQueryQuery - extends GraphQLQuery { +class SomeQueryQuery extends GraphQLQuery { SomeQueryQuery({this.variables}); @override @@ -315,28 +285,44 @@ class SomeQueryQuery directives: [], selectionSet: SelectionSetNode(selections: [ FieldNode( - name: NameNode(value: 's'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'i'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'list'), + name: NameNode(value: 'someQuery'), alias: null, arguments: [ ArgumentNode( name: NameNode(value: 'intsNonNullable'), value: - VariableNode(name: NameNode(value: 'intsNonNullable'))) + VariableNode(name: NameNode(value: 'intsNonNullable'))), + ArgumentNode( + name: NameNode(value: 'stringNullable'), + value: + VariableNode(name: NameNode(value: 'stringNullable'))) ], directives: [], - selectionSet: null) + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 's'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'i'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'list'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'intsNonNullable'), + value: VariableNode( + name: NameNode(value: 'intsNonNullable'))) + ], + directives: [], + selectionSet: null) + ])) ])) ]); @@ -349,8 +335,8 @@ class SomeQueryQuery @override List get props => [document, operationName, variables]; @override - SomeQuery$SomeObject parse(Map json) => - SomeQuery$SomeObject.fromJson(json); + SomeQuery$Query parse(Map json) => + SomeQuery$Query.fromJson(json); } ''', }, @@ -359,103 +345,50 @@ class SomeQueryQuery }); test('The selection from query can nest', () async { - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({ + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.grqphql', + 'queries_glob': 'queries/**.graphql', 'output': 'lib/some_query.dart', } ] })); - final GraphQLSchema schema = GraphQLSchema( - queryType: GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 's', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'o', - type: GraphQLType( - name: 'SomeObject', kind: GraphQLTypeKind.OBJECT)), - ]), - GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'st', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'ob', - type: GraphQLType( - kind: GraphQLTypeKind.LIST, - ofType: GraphQLType( - name: 'AnotherObject', - kind: GraphQLTypeKind.OBJECT))), - ]), - GraphQLType( - name: 'AnotherObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'str', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); - - final document = ''' - query some_query { - s - o { - st - ob { - str - } - } - } - '''; anotherBuilder.onBuild = expectAsync1((definition) { final libraryDefinition = LibraryDefinition(basename: r'some_query', queries: [ QueryDefinition( queryName: r'some_query', - queryType: r'SomeQuery$Query', + queryType: r'SomeQuery$Result', classes: [ ClassDefinition( - name: r'SomeQuery$Query$SomeObject$AnotherObject', + name: r'SomeQuery$Result$SomeObject$AnotherObject', properties: [ ClassProperty( type: r'String', name: r'str', isOverride: false) ], typeNameField: r'__typename'), ClassDefinition( - name: r'SomeQuery$Query$SomeObject', + name: r'SomeQuery$Result$SomeObject', properties: [ ClassProperty( type: r'String', name: r'st', isOverride: false), ClassProperty( type: - r'List', + r'List', name: r'ob', isOverride: false) ], typeNameField: r'__typename'), ClassDefinition( - name: r'SomeQuery$Query', + name: r'SomeQuery$Result', properties: [ ClassProperty( type: r'String', name: r's', isOverride: false), ClassProperty( - type: r'SomeQuery$Query$SomeObject', + type: r'SomeQuery$Result$SomeObject', name: r'o', isOverride: false) ], @@ -466,11 +399,41 @@ class SomeQueryQuery expect(definition, libraryDefinition); }, count: 1); + final document = ''' + query some_query { + s + o { + st + ob { + str + } + } + } + '''; + await testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), - 'a|some_query.graphql': document, + 'a|api.schema.grqphql': r''' + schema { + query: Result + } + + type Result { + s: String + o: SomeObject + } + + type SomeObject { + st: String + ob: [AnotherObject] + } + + type AnotherObject { + str: String + } + ''', + 'a|queries/some_query.graphql': document, }, outputs: { 'a|lib/some_query.dart': r'''// GENERATED CODE - DO NOT MODIFY BY HAND @@ -481,51 +444,51 @@ import 'package:gql/ast.dart'; part 'some_query.g.dart'; @JsonSerializable(explicitToJson: true) -class SomeQuery$Query$SomeObject$AnotherObject with EquatableMixin { - SomeQuery$Query$SomeObject$AnotherObject(); +class SomeQuery$Result$SomeObject$AnotherObject with EquatableMixin { + SomeQuery$Result$SomeObject$AnotherObject(); - factory SomeQuery$Query$SomeObject$AnotherObject.fromJson( + factory SomeQuery$Result$SomeObject$AnotherObject.fromJson( Map json) => - _$SomeQuery$Query$SomeObject$AnotherObjectFromJson(json); + _$SomeQuery$Result$SomeObject$AnotherObjectFromJson(json); String str; @override List get props => [str]; Map toJson() => - _$SomeQuery$Query$SomeObject$AnotherObjectToJson(this); + _$SomeQuery$Result$SomeObject$AnotherObjectToJson(this); } @JsonSerializable(explicitToJson: true) -class SomeQuery$Query$SomeObject with EquatableMixin { - SomeQuery$Query$SomeObject(); +class SomeQuery$Result$SomeObject with EquatableMixin { + SomeQuery$Result$SomeObject(); - factory SomeQuery$Query$SomeObject.fromJson(Map json) => - _$SomeQuery$Query$SomeObjectFromJson(json); + factory SomeQuery$Result$SomeObject.fromJson(Map json) => + _$SomeQuery$Result$SomeObjectFromJson(json); String st; - List ob; + List ob; @override List get props => [st, ob]; - Map toJson() => _$SomeQuery$Query$SomeObjectToJson(this); + Map toJson() => _$SomeQuery$Result$SomeObjectToJson(this); } @JsonSerializable(explicitToJson: true) -class SomeQuery$Query with EquatableMixin { - SomeQuery$Query(); +class SomeQuery$Result with EquatableMixin { + SomeQuery$Result(); - factory SomeQuery$Query.fromJson(Map json) => - _$SomeQuery$QueryFromJson(json); + factory SomeQuery$Result.fromJson(Map json) => + _$SomeQuery$ResultFromJson(json); String s; - SomeQuery$Query$SomeObject o; + SomeQuery$Result$SomeObject o; @override List get props => [s, o]; - Map toJson() => _$SomeQuery$QueryToJson(this); + Map toJson() => _$SomeQuery$ResultToJson(this); } ''', }, @@ -534,42 +497,26 @@ class SomeQuery$Query with EquatableMixin { }); test('Query selections can be aliased', () async { - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({ + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.graphql', + 'queries_glob': 'queries/**.graphql', 'output': 'lib/some_query.dart', } ] })); - final GraphQLSchema schema = GraphQLSchema( - queryType: GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Query', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 's', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - GraphQLField( - name: 'st', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); anotherBuilder.onBuild = expectAsync1((definition) { final libraryDefinition = LibraryDefinition(basename: r'some_query', queries: [ QueryDefinition( queryName: r'some_query', - queryType: r'SomeQuery$Query', + queryType: r'SomeQuery$Result', classes: [ ClassDefinition( - name: r'SomeQuery$Query', + name: r'SomeQuery$Result', properties: [ ClassProperty( type: r'String', @@ -588,8 +535,23 @@ class SomeQuery$Query with EquatableMixin { await testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), - 'a|some_query.graphql': + 'a|api.schema.graphql': r''' + schema { + query: Result + } + + type Result { + s: String + st: String + } + ''', + 'a|queries/some_query.graphql': r''' + query some_query { + firstName: s, + lastName: st + } + ''', + 'a|queries/some_query.graphql': 'query some_query { firstName: s, lastName: st }', }, outputs: { @@ -601,11 +563,11 @@ import 'package:gql/ast.dart'; part 'some_query.g.dart'; @JsonSerializable(explicitToJson: true) -class SomeQuery$Query with EquatableMixin { - SomeQuery$Query(); +class SomeQuery$Result with EquatableMixin { + SomeQuery$Result(); - factory SomeQuery$Query.fromJson(Map json) => - _$SomeQuery$QueryFromJson(json); + factory SomeQuery$Result.fromJson(Map json) => + _$SomeQuery$ResultFromJson(json); String firstName; @@ -613,7 +575,7 @@ class SomeQuery$Query with EquatableMixin { @override List get props => [firstName, lastName]; - Map toJson() => _$SomeQuery$QueryToJson(this); + Map toJson() => _$SomeQuery$ResultToJson(this); } ''', }, @@ -626,8 +588,8 @@ class SomeQuery$Query with EquatableMixin { 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.graphql', + 'queries_glob': 'queries/**.graphql', 'output': 'lib/some_query.dart', }, ], @@ -646,44 +608,7 @@ class SomeQuery$Query with EquatableMixin { ], }); - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(builderOptions); - - final GraphQLSchema schema = GraphQLSchema( - queryType: GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - ), - types: [ - GraphQLType( - name: 'BigDecimal', - kind: GraphQLTypeKind.SCALAR, - ), - GraphQLType( - name: 'DateTime', - kind: GraphQLTypeKind.SCALAR, - ), - GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'bigDecimal', - type: GraphQLType( - name: 'BigDecimal', - kind: GraphQLTypeKind.SCALAR, - ), - ), - GraphQLField( - name: 'dateTime', - type: GraphQLType( - name: 'DateTime', - kind: GraphQLTypeKind.SCALAR, - ), - ), - ], - ), - ]); + final anotherBuilder = graphQLQueryBuilder(builderOptions); anotherBuilder.onBuild = expectAsync1((definition) { final libraryDefinition = @@ -716,8 +641,20 @@ class SomeQuery$Query with EquatableMixin { await testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), - 'a|some_query.graphql': 'query some_query { bigDecimal, dateTime }', + 'a|api.schema.graphql': r''' + scalar BigDecimal + + schema { + query: SomeObject + } + + type SomeObject { + bigDecimal: BigDecimal + dateTime: DateTime + } + ''', + 'a|queries/some_query.graphql': + 'query some_query { bigDecimal, dateTime }', }, outputs: { 'a|lib/some_query.dart': r'''// GENERATED CODE - DO NOT MODIFY BY HAND @@ -750,32 +687,16 @@ class SomeQuery$SomeObject with EquatableMixin { }); test('Query name (pascal casing)', () async { - final GraphQLQueryBuilder anotherBuilder = - graphQLQueryBuilder(BuilderOptions({ + final anotherBuilder = graphQLQueryBuilder(BuilderOptions({ 'generate_helpers': false, 'schema_mapping': [ { - 'schema': 'api.schema.json', - 'queries_glob': '**.graphql', + 'schema': 'api.schema.graphql', + 'queries_glob': 'queries/**.graphql', 'output': 'lib/pascal_casing_query.dart', } ] })); - final GraphQLSchema schema = GraphQLSchema( - queryType: GraphQLType( - name: 'PascalCasingQuery', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'PascalCasingQuery', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 's', - type: GraphQLType( - name: 'String', kind: GraphQLTypeKind.SCALAR)), - ]), - ]); anotherBuilder.onBuild = expectAsync1((definition) { final libraryDefinition = @@ -800,8 +721,16 @@ class SomeQuery$SomeObject with EquatableMixin { await testBuilder( anotherBuilder, { - 'a|api.schema.json': jsonFromSchema(schema), - 'a|pascal_casing_query.query.graphql': + 'a|api.schema.graphql': r''' + schema { + query: PascalCasingQuery + } + + type PascalCasingQuery { + s: String + } + ''', + 'a|queries/pascal_casing_query.graphql': 'query PascalCasingQuery { s }', }, outputs: { diff --git a/test/query_generator/scalars/custom_scalars_test.dart b/test/query_generator/scalars/custom_scalars_test.dart index 3ef2a54d..815318dc 100644 --- a/test/query_generator/scalars/custom_scalars_test.dart +++ b/test/query_generator/scalars/custom_scalars_test.dart @@ -1,5 +1,4 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -11,9 +10,19 @@ void main() { 'If they can be converted to a simple dart class', () async => testGenerator( query: 'query query { a }', + schema: r''' + scalar MyUuid + + schema { + query: SomeObject + } + + type SomeObject { + a: MyUuid + } + ''', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schemaWithCustomScalar, builderOptionsMap: { 'scalar_mapping': [ { @@ -30,9 +39,19 @@ void main() { 'When they need custom parser functions', () async => testGenerator( query: 'query query { a }', + schema: r''' + scalar MyUuid + + schema { + query: SomeObject + } + + type SomeObject { + a: MyUuid + } + ''', libraryDefinition: libraryDefinitionWithCustomParserFns, generatedFile: generatedFileWithCustomParserFns, - typedSchema: schemaWithCustomScalar, builderOptionsMap: { 'scalar_mapping': [ { @@ -49,9 +68,19 @@ void main() { 'When they need custom imports', () async => testGenerator( query: 'query query { a }', + schema: r''' + scalar MyUuid + + schema { + query: SomeObject + } + + type SomeObject { + a: MyUuid + } + ''', libraryDefinition: libraryDefinitionWithCustomImports, generatedFile: generatedFileWithCustomImports, - typedSchema: schemaWithCustomScalar, builderOptionsMap: { 'scalar_mapping': [ { @@ -69,19 +98,8 @@ void main() { }); } -final schemaWithCustomScalar = GraphQLSchema( - queryType: GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'MyUuid', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 'a', - type: GraphQLType(name: 'MyUuid', kind: GraphQLTypeKind.SCALAR), - ), - ]), - ]); - -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'query', queryType: r'Query$SomeObject', @@ -102,7 +120,7 @@ final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ generateHelpers: false) ]); -final libraryDefinitionWithCustomParserFns = +final LibraryDefinition libraryDefinitionWithCustomParserFns = LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'query', @@ -128,7 +146,7 @@ final libraryDefinitionWithCustomParserFns = r'package:example/src/custom_parser.dart' ]); -final libraryDefinitionWithCustomImports = +final LibraryDefinition libraryDefinitionWithCustomImports = LibraryDefinition(basename: r'query', queries: [ QueryDefinition( queryName: r'query', diff --git a/test/query_generator/scalars/scalars_test.dart b/test/query_generator/scalars/scalars_test.dart index 3266d433..da9c433b 100644 --- a/test/query_generator/scalars/scalars_test.dart +++ b/test/query_generator/scalars/scalars_test.dart @@ -1,5 +1,5 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; +import 'package:gql/language.dart'; import 'package:test/test.dart'; import '../../helpers.dart'; @@ -10,96 +10,71 @@ void main() { test( 'If they are defined on schema', () async => testGenerator( - query: 'query query { a, b, c, d, e }', + schema: r''' + scalar Int + scalar Float + scalar String + scalar Boolean + scalar ID + + schema { + query: SomeObject + } + + type SomeObject { + i: Int + f: Float + s: String + b: Boolean + id: ID + } + ''', + query: 'query some_query { i, f, s, b, id }', libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schemaWithScalars, ), ); test( 'If they are NOT explicitly defined on schema', () async => testGenerator( - query: 'query query { a, b, c, d, e }', + schema: r''' + schema { + query: SomeObject + } + + type SomeObject { + i: Int + f: Float + s: String + b: Boolean + id: ID + } + ''', + query: query, libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schemaWithoutScalars, ), ); }); }); } -final schemaWithScalars = GraphQLSchema( - queryType: GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Float', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'Boolean', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'ID', kind: GraphQLTypeKind.SCALAR), - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 'a', - type: GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'b', - type: GraphQLType(name: 'Float', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'c', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'd', - type: GraphQLType(name: 'Boolean', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'e', - type: GraphQLType(name: 'ID', kind: GraphQLTypeKind.SCALAR), - ), - ]), - ]); - -final schemaWithoutScalars = GraphQLSchema( - queryType: GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT, fields: [ - GraphQLField( - name: 'a', - type: GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'b', - type: GraphQLType(name: 'Float', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'c', - type: GraphQLType(name: 'String', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'd', - type: GraphQLType(name: 'Boolean', kind: GraphQLTypeKind.SCALAR), - ), - GraphQLField( - name: 'e', - type: GraphQLType(name: 'ID', kind: GraphQLTypeKind.SCALAR), - ), - ]), - ]); +final String query = 'query some_query { i, f, s, b, id }'; -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( - queryName: r'query', - queryType: r'Query$SomeObject', + document: parseString(query), + queryName: r'some_query', + queryType: r'SomeQuery$SomeObject', classes: [ - ClassDefinition(name: r'Query$SomeObject', properties: [ - ClassProperty(type: 'int', name: 'a'), - ClassProperty(type: 'double', name: 'b'), - ClassProperty(type: 'String', name: 'c'), - ClassProperty(type: 'bool', name: 'd'), - ClassProperty(type: 'String', name: 'e'), + ClassDefinition(name: r'SomeQuery$SomeObject', properties: [ + ClassProperty(type: 'int', name: 'i'), + ClassProperty(type: 'double', name: 'f'), + ClassProperty(type: 'String', name: 's'), + ClassProperty(type: 'bool', name: 'b'), + ClassProperty(type: 'String', name: 'id'), ]), ], ) @@ -113,24 +88,24 @@ import 'package:gql/ast.dart'; part 'query.g.dart'; @JsonSerializable(explicitToJson: true) -class Query$SomeObject with EquatableMixin { - Query$SomeObject(); +class SomeQuery$SomeObject with EquatableMixin { + SomeQuery$SomeObject(); - factory Query$SomeObject.fromJson(Map json) => - _$Query$SomeObjectFromJson(json); + factory SomeQuery$SomeObject.fromJson(Map json) => + _$SomeQuery$SomeObjectFromJson(json); - int a; + int i; - double b; + double f; - String c; + String s; - bool d; + bool b; - String e; + String id; @override - List get props => [a, b, c, d, e]; - Map toJson() => _$Query$SomeObjectToJson(this); + List get props => [i, f, s, b, id]; + Map toJson() => _$SomeQuery$SomeObjectToJson(this); } '''; diff --git a/test/query_generator/union_types_test.dart b/test/query_generator/union_types_test.dart index c165cc35..1d13a74f 100644 --- a/test/query_generator/union_types_test.dart +++ b/test/query_generator/union_types_test.dart @@ -1,5 +1,5 @@ import 'package:artemis/generator/data.dart'; -import 'package:artemis/schema/graphql.dart'; +import 'package:gql/language.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -9,79 +9,53 @@ void main() { test( 'On union types', () async => testGenerator( - query: - r'query some_query { o { __typename, ... on TypeA { a }, ... on TypeB { b } } }', + query: query, + schema: graphQLSchema, libraryDefinition: libraryDefinition, generatedFile: generatedFile, - typedSchema: schema, ), ); }); } -final graphQLSchema = ''' -schema { - query: SomeObject -} - -type SomeObject { - o: SomeUnion! -} - -union SomeUnion = TypeA | TypeB +final String query = r''' + query some_query { + o { + __typename, + ... on TypeA { + a + }, + ... on TypeB { + b + } + } + } +'''; -type TypeA { - a: Int -} +final String graphQLSchema = ''' + schema { + query: SomeObject + } -type TypeB { - b: Int -} + type SomeObject { + o: SomeUnion + } + + union SomeUnion = TypeA | TypeB + + type TypeA { + a: Int + } + + type TypeB { + b: Int + } '''; -final schema = GraphQLSchema( - queryType: GraphQLType(name: 'SomeObject', kind: GraphQLTypeKind.OBJECT), - types: [ - GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR), - GraphQLType( - name: 'SomeObject', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'o', - type: - GraphQLType(name: 'SomeUnion', kind: GraphQLTypeKind.UNION)), - ], - ), - GraphQLType( - name: 'TypeA', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'a', - type: GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR)), - ], - ), - GraphQLType( - name: 'TypeB', - kind: GraphQLTypeKind.OBJECT, - fields: [ - GraphQLField( - name: 'b', - type: GraphQLType(name: 'Int', kind: GraphQLTypeKind.SCALAR)), - ], - ), - GraphQLType( - name: 'SomeUnion', - kind: GraphQLTypeKind.UNION, - possibleTypes: [ - GraphQLType(name: 'TypeA', kind: GraphQLTypeKind.OBJECT), - GraphQLType(name: 'TypeB', kind: GraphQLTypeKind.OBJECT), - ], - ), - ]); -final libraryDefinition = LibraryDefinition(basename: r'query', queries: [ +final LibraryDefinition libraryDefinition = + LibraryDefinition(basename: r'query', queries: [ QueryDefinition( + document: parseString(query), queryName: r'some_query', queryType: r'SomeQuery$SomeObject', classes: [