import 'dart:mirrors'; import 'package:angel_serialize/angel_serialize.dart'; import 'package:graphql_schema/graphql_schema.dart'; import 'package:recase/recase.dart'; /// Reflects upon a given [type] and dynamically generates a [GraphQLType] that corresponds to it. /// /// This function is aware of the annotations from `package:angel_serialize`, and works seamlessly /// with them. GraphQLType objectTypeFromDartType(Type type, [List typeArguments]) { if (type == bool) { return graphQLBoolean; } else if (type == int) { return graphQLInt; } else if (type == double || type == num) { return graphQLFloat; } else if (type == String) { return graphQLString; } var mirror = reflectType(type, typeArguments); if (mirror is! ClassMirror) { throw new StateError( '$type is not a class, and therefore cannot be converted into a GraphQL object type.'); } return objectTypeFromClassMirror(mirror as ClassMirror); } GraphQLObjectType objectTypeFromClassMirror(ClassMirror mirror) { var fields = []; for (var name in mirror.instanceMembers.keys) { var methodMirror = mirror.instanceMembers[name]; var exclude = _getExclude(name, methodMirror); var canAdd = exclude?.canSerialize != true; if (methodMirror.isGetter && canAdd) { fields.add(fieldFromGetter(name, methodMirror, exclude, mirror)); } } return objectType(MirrorSystem.getName(mirror.simpleName), fields); } GraphQLField fieldFromGetter( Symbol name, MethodMirror mirror, Exclude exclude, ClassMirror clazz) { var type = objectTypeFromDartType(mirror.returnType.reflectedType, mirror.returnType.typeArguments.map((t) => t.reflectedType).toList()); var nameString = _getSerializedName(name, mirror, clazz); var defaultValue = _getDefaultValue(mirror); if (nameString == 'id' && _autoNames(clazz)) { type = graphQLId; } return field( nameString, type: type, resolve: (obj, _) { if (obj is Map && exclude?.canSerialize != true) { return obj[nameString]; } else if (obj != null && exclude?.canSerialize != true) { return reflect(obj).getField(name); } else { return defaultValue; } }, ); } Exclude _getExclude(Symbol name, MethodMirror mirror) { for (var obj in mirror.metadata) { if (obj.reflectee is Exclude) { var exclude = obj.reflectee as Exclude; return exclude; } } return null; } String _getSerializedName(Symbol name, MethodMirror mirror, ClassMirror clazz) { // First search for an @Alias() for (var obj in mirror.metadata) { if (obj.reflectee is Alias) { var alias = obj.reflectee as Alias; return alias.name; } } // Next, search for a @Serializable() for (var obj in clazz.metadata) { if (obj.reflectee is Serializable) { var ann = obj.reflectee as Serializable; if (ann.autoSnakeCaseNames != false) { return new ReCase(MirrorSystem.getName(name)).snakeCase; } } } return MirrorSystem.getName(name); } dynamic _getDefaultValue(MethodMirror mirror) { // Search for a @DefaultValue for (var obj in mirror.metadata) { if (obj.reflectee is DefaultValue) { var ann = obj.reflectee as DefaultValue; return ann.value; } } return null; } bool _autoNames(ClassMirror clazz) { // Search for a @Serializable() for (var obj in clazz.metadata) { if (obj.reflectee is Serializable) { var ann = obj.reflectee as Serializable; return ann.autoIdAndDateFields != false; } } return false; }