private static bool TryGetRemoteSchema(string remoteLocation, RemoteSchemaFetcher remoteSchemaFetcher, out Introspection.Schema schema)
        {
            schema = null;

            try
            {
                schema = remoteSchemaFetcher(remoteLocation);
            }
            catch (Exception)
            {
                // TODO: log stuff
            }

            return(schema != null);
        }
        private static Introspection.Schema FetchRemoteServerSchema(string remote, RemoteSchemaFetcher remoteSchemaFetcher)
        {
            // lock types mutex
            lock (FetchRemoteServerSchemaMutex)
            {
                // did someone else fetch it while we were waiting?
                if (!RemoteServerSchemas.TryGetValue(remote, out var schema))
                {
                    //  fetch remote type information
                    if (!TryGetRemoteSchema(remote, remoteSchemaFetcher, out schema))
                    {
                        // if it fails: fail
                        // TODO: fail better
                        throw new Exception("Failed to get remote schema");
                    }

                    // if it succeeds: save type information for all types
                    RemoteServerSchemas.AddOrUpdate(remote, schema, (key, old) => schema);
                }

                return(schema);
            }
        }
        public static async Task <IEnumerable <Type> > LoadRemotes(IEnumerable <RemoteDescriptor> remotes, Func <TypeElement, bool> typeFilter = null, RemoteSchemaFetcher remoteSchemaFetcher = null)
        {
            if (typeFilter == null)
            {
                typeFilter = t => !t.Name.StartsWith("__") && t.Kind != TypeElementTypeKind.Scalar;
            }

            if (remoteSchemaFetcher == null)
            {
                remoteSchemaFetcher = FetchRemoteSchemaViaHttp;
            }

            var parentConstructor            = typeof(RemoteLiteralGraphType).GetConstructor(new[] { typeof(string), typeof(string) });
            var metadataAttributeConstructor = typeof(RemoteLiteralGraphTypeMetadataAttribute).GetConstructor(new[] { typeof(string), typeof(string), typeof(string) });

            // Convert each remote into a new assembly asynchronously
            var tasks = remotes
                        .Select(remote =>
            {
                return(Task.Run(() =>
                {
                    var url = remote.Url;
                    var schema = FetchRemoteServerSchema(url, remoteSchemaFetcher);

                    var assemblyName = new AssemblyName($"GraphQL.Dynamic.RemoteLiteralGraphTypes{Guid.NewGuid().ToString("N")}");
                    var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);

                    var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, $"{assemblyName.Name}.dll");

                    var types = schema.Types
                                .Where(typeFilter)
                                .Select(schemaType =>
                    {
                        var typeName = schemaType.Name;

                        // [RemoteLiteralGraphTypeMetadata(<remote.Moniker>, <remote.Url>, <typeName>)]
                        // public class GeneratedRemoteLiteralGraphType : RemoteLiteralGraphType
                        var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class, typeof(RemoteLiteralGraphType));
                        typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(metadataAttributeConstructor, new[] { remote.Moniker, url, typeName }));

                        // public GeneratedRemoteLiteralGraphType(): base(<remote>, <typeName>) { }
                        var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
                        var ctorGenerator = ctorBuilder.GetILGenerator();
                        ctorGenerator.Emit(OpCodes.Ldarg_0);
                        ctorGenerator.Emit(OpCodes.Ldstr, url);
                        ctorGenerator.Emit(OpCodes.Ldstr, typeName);
                        ctorGenerator.Emit(OpCodes.Call, parentConstructor);
                        ctorGenerator.Emit(OpCodes.Ret);

                        return typeBuilder.CreateType();
                    })
                                .ToList();

                    // Update cache
                    var typeSet = new HashSet <Type>(types);
                    RemoteServerTypes.AddOrUpdate(url, typeSet, (key, old) => typeSet);

                    return types;
                }));
            })
                        .ToList();

            // Wait for schema & type resolution
            var jaggedTypes = await Task.WhenAll(tasks);

            // Flatten
            return(jaggedTypes.SelectMany(t => t).ToList());
        }
        public static async Task <IEnumerable <Type> > LoadRemotes(IEnumerable <RemoteDescriptor> remotes, Func <TypeElement, bool> typeFilter = null, RemoteSchemaFetcher remoteSchemaFetcher = null)
        {
            if (typeFilter == null)
            {
                typeFilter = t => !t.Name.StartsWith("__") && t.Kind != TypeElementTypeKind.Scalar;
            }

            if (remoteSchemaFetcher == null)
            {
                remoteSchemaFetcher = FetchRemoteSchemaViaHttp;
            }

            var parentConstructor            = typeof(RemoteLiteralGraphType).GetConstructor(new [] { typeof(string), typeof(string) });
            var metadataAttributeConstructor = typeof(RemoteLiteralGraphTypeMetadataAttribute).GetConstructor(new [] { typeof(string), typeof(string), typeof(string) });

            // Convert each remote into a new assembly asynchronously
            var tasks = remotes
                        .Select(remote =>
            {
                return(Task.Run(() =>
                {
                    var url = remote.Url;
                    var schema = FetchRemoteServerSchema(url, remoteSchemaFetcher);

                    var types = schema.Types
                                .Where(typeFilter)
                                .Select(schemaType =>
                    {
                        var typeName = schemaType.Name;

                        // Create CompilationUnitSyntax
                        var syntaxFactory = SyntaxFactory.CompilationUnit();

                        // Add System using statement
                        syntaxFactory = syntaxFactory.AddUsings(
                            SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("GraphQL.Dynamic.Types.LiteralGraphType"))
                            );

                        //  Create a class
                        var classDeclaration = SyntaxFactory.ClassDeclaration(typeName);

                        // Add the public modifier
                        classDeclaration = classDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));

                        var name = SyntaxFactory.ParseName("RemoteLiteralGraphTypeMetadata");
                        var arguments = SyntaxFactory.ParseAttributeArgumentList($"(\"{remote.Moniker}\", \"{remote.Url}\", \"{typeName}\")");
                        var attribute = SyntaxFactory.Attribute(name, arguments);

                        var attributeList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(attribute))
                                            .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed);
                        classDeclaration = classDeclaration.AddAttributeLists(attributeList);

                        classDeclaration = classDeclaration.AddBaseListTypes(
                            SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("RemoteLiteralGraphType")));

                        var ctorDeclaration = SyntaxFactory.ConstructorDeclaration(typeName)
                                              .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                                              .WithInitializer(
                            SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer)
                            .AddArgumentListArguments(
                                SyntaxFactory.Argument(SyntaxFactory.IdentifierName($"\"{url}\"")),
                                SyntaxFactory.Argument(SyntaxFactory.IdentifierName($"\"{typeName}\""))
                                )
                            )
                                              .WithBody(SyntaxFactory.Block());

                        // Add the field, the property and method to the class.
                        classDeclaration = classDeclaration.AddMembers(ctorDeclaration);

                        syntaxFactory = syntaxFactory.AddMembers(classDeclaration);

                        var referenceAssemblies = CollectReferences();
                        referenceAssemblies.Add(MetadataReference.CreateFromFile(typeof(RemoteLiteralGraphType).Assembly.Location));
                        referenceAssemblies.Add(MetadataReference.CreateFromFile(typeof(ObjectGraphType).Assembly.Location));
                        referenceAssemblies.Add(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));

                        var compilation = CSharpCompilation.Create($"GraphQL.Dynamic.RemoteLiteralGraphTypes.{typeName}-{Guid.NewGuid().ToString("N")}",
                                                                   options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
                                                                   syntaxTrees: new [] { CSharpSyntaxTree.ParseText(syntaxFactory.NormalizeWhitespace().ToFullString()) },
                                                                   references: referenceAssemblies
                                                                   );

                        EmitResult emitResult;

                        using (var ms = new MemoryStream())
                        {
                            emitResult = compilation.Emit(ms);
                            if (emitResult.Success)
                            {
                                var assembly = Assembly.Load(ms.GetBuffer());
                                return assembly.GetType(typeName);
                            }
                            else
                            {
                                foreach (var error in emitResult.Diagnostics)
                                {
                                    Debug.WriteLine(error.GetMessage());
                                }
                            }
                        }

                        return null;
                    })
                                .ToList();

                    // Update cache
                    var typeSet = new HashSet <Type>(types);
                    RemoteServerTypes.AddOrUpdate(url, typeSet, (key, old) => typeSet);

                    return types;
                }));
            })
                        .ToList();

            // Wait for schema & type resolution
            var jaggedTypes = await Task.WhenAll(tasks);

            // Flatten
            return(jaggedTypes.SelectMany(t => t).ToList());
        }