public static void Introspect(CommandLineApplication app) { CommandArgument location = app.Argument("location", "The source of the introspection, be it a http endpoint or a dll or event a json file", true); CommandOption query = app.Option("--query <queryType>", "a pattern to discover a query type, can be eather a type or a pointer to types deccerated with [AttributeName]", CommandOptionType.MultipleValue); CommandOption mutation = app.Option("--mutation <mutationType>", "a mutation to discover a query type, can be eather a type or a pointer to types deccerated with [AttributeName]", CommandOptionType.MultipleValue); CommandOption output = app.Option("--output <path>", "the path to output to, if no provided it will output to console ", CommandOptionType.SingleValue); app.OnExecute(() => { #if NET461 AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var name = args.Name.Split(new[] { ',' }, 2)[0]; var res = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(x => { return(x.GetName().Name == name); }); return(res); }; #else System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (a, b) => { return(null); }; #endif // these are the ones we scan for the correct types, IEnumerable <string> dlls = GlobExpander.FindFiles(location.Values); //all dlls in directoris next to any of the source dlls IEnumerable <string> directoreistoLoad = dlls.Select(x => Path.GetDirectoryName(x)).Distinct(); // these are all the ones we need to load into the appdomain List <string> finalListToLoad = directoreistoLoad.SelectMany(x => Directory.EnumerateFiles(x, "*.dll")).Union(directoreistoLoad.SelectMany(x => Directory.EnumerateFiles(x, "*.exe"))).ToList(); List <Assembly> searchableAssemblies = new List <Assembly>(); List <Assembly> allAssemblies = new List <Assembly>(); List <string> additionalAssembliesToLoad = new List <string>(); foreach (string toLoad in finalListToLoad) { try { #if NET461 var assembly = Assembly.LoadFile(toLoad); allAssemblies.Add(assembly); #else var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(toLoad); allAssemblies.Add(assembly); #endif if (dlls.Contains(toLoad)) { searchableAssemblies.Add(assembly); } } catch { } } var hcLib = allAssemblies.FirstOrDefault(x => x.GetName().Name == "HotChocolate.Execution"); var abstractionsAssembly = allAssemblies.FirstOrDefault(x => x.GetName().Name == "Microsoft.Extensions.Hosting.Abstractions"); var hostBuilderType = abstractionsAssembly?.GetType("Microsoft.Extensions.Hosting.IHostBuilder"); var hostBuilder_BuildMethod = hostBuilderType?.GetMethod("Build"); var hostType = hostBuilder_BuildMethod?.ReturnType; var serviceProviderProp = hostType?.GetProperty("Services"); var serviceProviderType = serviceProviderProp?.PropertyType; var GetServiceMethod = serviceProviderType?.GetMethod("GetService", new Type[] { typeof(Type) }); var executerType = hcLib?.GetType("HotChocolate.Execution.IRequestExecutorResolver"); var GetRequestExecutorAsyncMethod = executerType?.GetMethod("GetRequestExecutorAsync"); var ValueTask_IRequestExecutor = GetRequestExecutorAsyncMethod?.ReturnType; var returnValue = ValueTask_IRequestExecutor?.GetProperty("Result"); var IRequestExecutorType = returnValue?.PropertyType; var schemaProprety = IRequestExecutorType?.GetProperty("Schema"); // load the dlll + all other dlls from the // we want to see if we have loaded in graphql.conventions bool hasSchema = false; string schema = ""; if (hcLib != null && abstractionsAssembly != null && GetServiceMethod != null && schemaProprety != null) { var sourceDlls = allAssemblies.Where(x => dlls.Contains(x.Location)); foreach (var dll in sourceDlls) { var entryClass = dll.EntryPoint.DeclaringType; var method = entryClass.GetMethod("CreateHostBuilder", new Type[] { typeof(string[]) }); if (method != null) { var hostBuilder = method.Invoke(null, new object[] { new string[0] }); var host = hostBuilder_BuildMethod.Invoke(hostBuilder, null); var provider = serviceProviderProp.GetMethod.Invoke(host, null); var executer = GetServiceMethod.Invoke(provider, new object[] { executerType }); if (executer != null) { var executeorTask = GetRequestExecutorAsyncMethod.Invoke(executer, null); var executor = returnValue.GetMethod.Invoke(executeorTask, null); var schemaObj = schemaProprety.GetMethod.Invoke(executor, null); schema = schemaObj.ToString(); hasSchema = true; break; } } } } if (!hasSchema) { Console.Error.WriteLine("HotChocolate and uses the aspnetcore generic host conventions of exposing a `CreateHostBuilder` method from the same class as the `Main` method."); return(-1); } if (output.HasValue()) { var fullOutputPath = Path.GetFullPath(output.Value()); Directory.CreateDirectory(Path.GetDirectoryName(fullOutputPath)); File.WriteAllText(fullOutputPath, schema); } else { Console.Write(schema); } return(0); }); }
private bool ExpandSettings(string path, SimpleSourceFile file, bool overwrite = false) { var root = Path.GetDirectoryName(path); var settingsJson = File.ReadAllText(path); var settings = Newtonsoft.Json.JsonConvert.DeserializeObject <SimpleSettings>(settingsJson); if (!string.IsNullOrWhiteSpace(settings.Class) && (string.IsNullOrWhiteSpace(file.ClassName) || overwrite)) { file.ClassName = settings.Class; } if (!string.IsNullOrWhiteSpace(settings.TypeNameDirective) && (string.IsNullOrWhiteSpace(file.TypeNameDirective) || overwrite)) { file.TypeNameDirective = settings.TypeNameDirective; } if (!string.IsNullOrWhiteSpace(settings.Output) && (string.IsNullOrWhiteSpace(file.OutputPath) || overwrite)) { file.OutputPath = GenerateFullPath(root, settings.Output); } if (!string.IsNullOrWhiteSpace(settings.Format) && (string.IsNullOrWhiteSpace(file.Format) || overwrite)) { file.Format = settings.Format; } if (settings.Template != null) { foreach (var t in settings.Template) { var templateFiles = GlobExpander.FindFiles(root, t); file.Templates.AddRange(templateFiles); } } if (settings.TemplateSettings != null) { foreach (var t in settings.TemplateSettings) { if (!file.TemplateSettings.TryGetValue(t.Key, out _)) { // set if doesn't exist file.TemplateSettings[t.Key] = t.Value; } } } if (settings.Include != null) { foreach (var t in settings.Include) { var files = GlobExpander.FindFiles(root, t); file.Includes.AddRange(files); } } if (string.IsNullOrWhiteSpace(file.SchemaSource?.Location) || overwrite) { if (settings.Schema != null && !string.IsNullOrWhiteSpace(settings.Schema.Location)) { file.SchemaSource = settings.Schema; if (file.SchemaSource.SchemaType() != SchemaSource.SchemaTypes.Http) { // we are not and url based location then it must be a path file.SchemaSource.Location = GlobExpander.FindFile(root, file.SchemaSource.Location); } } } if (settings.Root) { file.RootPath = root; } return(settings.Root); }
public IEnumerable <CodeGeneratorSettings> GenerateSettings(CodeGeneratorSettingsLoaderDefaults settings, IEnumerable <string> paths) { var root = Directory.GetCurrentDirectory(); // we need to multi pass the source files looking for items to load var toProcess = new Queue <string>(paths.SelectMany(x => GlobExpander.FindFiles(root, x))); List <SimpleSourceFile> sourceFiles = new List <SimpleSourceFile>(); List <string> processedPaths = new List <string>(); while (toProcess.Any()) { var path = toProcess.Dequeue(); processedPaths.Add(path); var loaded = Load(path, settings); var newPaths = loaded.Includes.Where(x => !processedPaths.Contains(x)); foreach (var p in newPaths) { toProcess.Enqueue(p); } sourceFiles.Add(loaded); } var schemaFiles = sourceFiles.Select(x => x.SchemaSource?.Location); var grouped = sourceFiles .Where(x => !schemaFiles.Contains(x.Path)) // exclude files linked in a schemas .GroupBy(x => new { hash = x.SettingsHash() }).ToList(); return(grouped.Select(x => { var first = x.First(); var nspc = ""; var cn = first.ClassName; var idx = first.ClassName.LastIndexOf('.'); if (idx > 0) { cn = first.ClassName.Substring(idx); nspc = first.ClassName.Substring(0, idx); } cn = cn.Trim('.'); nspc = nspc.Trim('.'); var templates = DefaultTemplates(first.OutputPath); templates.AddRange(x.First().Templates); return new CodeGeneratorSettings { ClassName = cn, Namespace = nspc, OutputPath = first.OutputPath, RootPath = first.RootPath, TypeNameDirective = first.TypeNameDirective, SourceFiles = x.Select(p => new NamedSource { Body = p.Body, Path = p.Path }).ToList(), Schema = x.First().SchemaSource, Templates = templates, TemplateSettings = first.TemplateSettings }; }).ToList()); }
private SimpleSourceFile Load(string path, CodeGeneratorSettingsLoaderDefaults settings) { // path must be a real full path by here var gql = File.ReadAllText(path); // lets discover / cache all settings files from this point up the stack and root them out var file = new SimpleSourceFile() { Path = path, Body = gql }; var root = Path.GetDirectoryName(path); var matches = regex.Matches(gql); var pairs = matches.OfType <Match>().Select(m => (key: m.Groups[1].Value.ToLower(), val: m.Groups[2].Value)).ToList(); foreach (var m in pairs) { // process them in order to later directives overwrite previous ones switch (m.key) { case "schema": file.SchemaSource = file.SchemaSource ?? new SchemaSource(); file.SchemaSource.Location = GenerateFullPath(root, m.val); break; case "schema.querytype": file.SchemaSource = file.SchemaSource ?? new SchemaSource(); file.SchemaSource.QueryType.Add(m.val); break; case "schema.mutationtype": file.SchemaSource = file.SchemaSource ?? new SchemaSource(); file.SchemaSource.MutationType.Add(m.val); break; case "output": file.OutputPath = GenerateFullPath(root, m.val); break; case "class": file.ClassName = m.val; break; case "typedirective": file.TypeNameDirective = m.val; break; case "format": file.Format = m.val; break; case "settings": ExpandSettings(GenerateFullPath(root, m.val), file); break; case "template": var templateFiles = GlobExpander.FindFiles(root, m.val); file.Templates.AddRange(templateFiles); break; case "include": var includeFiles = GlobExpander.FindFiles(root, m.val); file.Includes.AddRange(includeFiles); break; default: break; } } LoadSettingsTree(file); if (File.Exists(settings.OverridesPath)) { ExpandSettings(settings.OverridesPath, file, true); } if (string.IsNullOrWhiteSpace(file.TypeNameDirective)) { file.TypeNameDirective = "__codeGenTypeName"; } if (string.IsNullOrWhiteSpace(file.Format)) { if (!string.IsNullOrWhiteSpace(file.OutputPath)) { file.Format = Path.GetExtension(file.OutputPath).Trim('.').ToLower(); } if (string.IsNullOrWhiteSpace(file.Format)) { file.Format = settings.Format; } } if (string.IsNullOrWhiteSpace(file.OutputPath)) { if (string.IsNullOrWhiteSpace(settings.OutputPath)) { file.OutputPath = file.Path; } else { file.OutputPath = settings.OutputPath; } } if (!Path.GetExtension(file.OutputPath).Trim('.').Equals(file.Format, StringComparison.OrdinalIgnoreCase)) { file.OutputPath += "." + file.Format; } if (string.IsNullOrWhiteSpace(file.ClassName)) { file.ClassName = Path.GetFileNameWithoutExtension(file.Path); } settings.FixFile?.Invoke(file); file.OutputPath = file.OutputPath.Replace("{classname}", file.ClassName); return(file); }