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()); }