Exemplo n.º 1
0
        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);
        }