public GraphController()
        {
            var query = new RootQuery();

            Schema   = new RootSchema(query);
            Executor = new DocumentExecuter();
        }
Beispiel #2
0
        private int?Execute(CliContext context)
        {
            RootSchema   root  = context.RootSchema;
            CommandInput input = context.Input;

            // Try to get the command matching the input or fallback to default
            CommandSchema commandSchema = root.TryFindCommand(input.CommandName) ?? StubDefaultCommand.Schema;

            // TODO: is the problem below still valid?
            // TODO: is it poossible to overcome this (related to [!]) limitation of new mode system
            // Forbid to execute real default command in interactive mode without [!] directive.
            //if (!(commandSchema.IsHelpOptionAvailable && input.IsHelpOptionSpecified) &&
            //    _applicationLifetime.CurrentModeType == typeof(InteractiveMode) &&
            //    commandSchema.IsDefault && !hasDefaultDirective)
            //{
            //    commandSchema = StubDefaultCommand.Schema;
            //}

            // Update CommandSchema
            context.CommandSchema = commandSchema;

            // Get command instance (default values are used in help so we need command instance)
            ICommand instance = GetCommandInstance(commandSchema);

            context.Command = instance;

            // To avoid instantiating the command twice, we need to get default values
            // before the arguments are bound to the properties
            IReadOnlyDictionary <ArgumentSchema, object?> defaultValues = commandSchema.GetArgumentValues(instance);

            context.CommandDefaultValues = defaultValues;

            return(null);
        }
Beispiel #3
0
        /// <inheritdoc/>
        public void Write(CommandSchema command,
                          IReadOnlyDictionary <ArgumentSchema, object?> defaultValues)
        {
            RootSchema root = _context.RootSchema;
            IEnumerable <CommandSchema> childCommands = root.GetChildCommands(command.Name)
                                                        .OrderBy(x => x.Name);

            _console.ResetColor();
            _console.ForegroundColor = ConsoleColor.Gray;

            if (command.IsDefault)
            {
                WriteApplicationInfo();
            }

            WriteCommandDescription(command);
            WriteCommandUsage(root.Directives, command, childCommands);
            WriteCommandParameters(command);
            WriteCommandOptions(command, defaultValues);
            WriteModeRestrictionsManual(command);
            WriteCommandChildren(command, childCommands);
            WriteDirectivesManual(root.Directives);
            WriteCommandManual(command);

            WriteLine();
        }
Beispiel #4
0
        /// <summary>
        /// Parses input before pipeline execution.
        /// </summary>
        protected virtual async Task <int> ParseInput(IReadOnlyList <string> commandLineArguments,
                                                      RootSchema root)
        {
            //TODO: CommandInput.Parse as middleware step
            CommandInput input = CommandInputResolver.Parse(commandLineArguments, root.GetCommandNames());

            CliContext.Input = input;

            return(await ExecuteCommand());
        }
Beispiel #5
0
        public static RootSchema GetSchema()
        {
            RootSchema schema = new RootSchema().InitProperties();

            schema.AdditionalProperties = false;

            foreach (var extension in ExtensionInfo.Extensions)
            {
                schema.Properties.Add(extension.Name, new RootSchema()
                {
                    Type        = SchemaObjectType.Boolean,
                    Default     = false,
                    Description = "The '" + extension.Name + "' extension costs " + extension.Cost + " extension points."
                });
            }

            return(schema);
        }
Beispiel #6
0
        private int?Execute(CliContext context)
        {
            RootSchema   root  = context.RootSchema;
            CommandInput input = context.Input;

            // Try to get the command matching the input or fallback to default
            bool          hasDefaultDirective = input.HasDirective(BuiltInDirectives.Default);
            CommandSchema command             = root.TryFindCommand(input.CommandName, hasDefaultDirective) ?? StubDefaultCommand.Schema;

            // Forbid to execute real default command in interactive mode without [!] directive.
            if (!(command.IsHelpOptionAvailable && input.IsHelpOptionSpecified) &&
                context.IsInteractiveMode && command.IsDefault && !hasDefaultDirective)
            {
                command = StubDefaultCommand.Schema;
            }

            // Update CommandSchema
            context.CommandSchema = command;

            return(null);
        }
        private async Task RunInteractivelyAsync(RootSchema root)
        {
            IConsole console        = CliContext.Console;
            string   executableName = CliContext.Metadata.ExecutableName;

            do
            {
                string[]? commandLineArguments = GetInput(console, executableName);

                if (commandLineArguments is null)
                {
                    console.ResetColor();
                    return;
                }

                CommandInput input = CommandInputResolver.Parse(commandLineArguments, root.GetCommandNames());
                CliContext.Input = input; //TODO maybe refactor with some clever IDisposable class

                await ExecuteCommand();

                console.ResetColor();
            }while (!console.GetCancellationToken().IsCancellationRequested);
        }
        protected override async Task <int> ParseInput(IReadOnlyList <string> commandLineArguments,
                                                       RootSchema root)
        {
            CommandInput input = CommandInputResolver.Parse(commandLineArguments, root.GetCommandNames());

            CliContext.Input = input;

            if (input.IsInteractiveDirectiveSpecified)
            {
                CliContext.IsInteractiveMode = true;

                // we don't want to run default command for e.g. `[interactive]` but we want to run if there is sth else
                if (!input.IsDefaultCommandOrEmpty)
                {
                    await ExecuteCommand();
                }

                await RunInteractivelyAsync(root);

                return(ExitCodes.Success); // called after Ctrl+C
            }

            return(await ExecuteCommand());
        }
Beispiel #9
0
        /// <summary>
        /// Runs the application with specified command line arguments and environment variables, and returns the exit code.
        /// </summary>
        /// <remarks>
        /// If a <see cref="CommandException"/> is thrown during command execution, it will be handled and routed to the console.
        /// Additionally, if the debugger is not attached (i.e. the app is running in production), all other exceptions thrown within
        /// this method will be handled and routed to the console as well.
        /// </remarks>
        public async ValueTask <int> RunAsync(
            IReadOnlyList <string> commandLineArguments,
            IReadOnlyDictionary <string, string> environmentVariables)
        {
            try
            {
                var root  = RootSchema.Resolve(_configuration.CommandTypes);
                var input = CommandInput.Parse(commandLineArguments, root.GetCommandNames());

                // Debug mode
                if (_configuration.IsDebugModeAllowed && input.IsDebugDirectiveSpecified)
                {
                    await LaunchAndWaitForDebuggerAsync();
                }

                // Preview mode
                if (_configuration.IsPreviewModeAllowed && input.IsPreviewDirectiveSpecified)
                {
                    WriteCommandLineInput(input);
                    return(ExitCode.Success);
                }

                // Try to get the command matching the input or fallback to default
                var command =
                    root.TryFindCommand(input.CommandName) ??
                    root.TryFindDefaultCommand() ??
                    StubDefaultCommand.Schema;

                // Version option
                if (command.IsVersionOptionAvailable && input.IsVersionOptionSpecified)
                {
                    _console.Output.WriteLine(_metadata.VersionText);
                    return(ExitCode.Success);
                }

                // Get command instance (also used in help text)
                var instance = GetCommandInstance(command);

                // To avoid instantiating the command twice, we need to get default values
                // before the arguments are bound to the properties
                var defaultValues = command.GetArgumentValues(instance);

                // Help option
                if (command.IsHelpOptionAvailable && input.IsHelpOptionSpecified ||
                    command == StubDefaultCommand.Schema && !input.Parameters.Any() && !input.Options.Any())
                {
                    _helpTextWriter.Write(root, command, defaultValues);
                    return(ExitCode.Success);
                }

                // Bind arguments
                try
                {
                    command.Bind(instance, input, environmentVariables);
                }
                // This may throw exceptions which are useful only to the end-user
                catch (CliFxException ex)
                {
                    WriteError(ex.ToString());
                    _helpTextWriter.Write(root, command, defaultValues);

                    return(ExitCode.FromException(ex));
                }

                // Execute the command
                try
                {
                    await instance.ExecuteAsync(_console);

                    return(ExitCode.Success);
                }
                // Swallow command exceptions and route them to the console
                catch (CommandException ex)
                {
                    WriteError(ex.ToString());

                    if (ex.ShowHelp)
                    {
                        _helpTextWriter.Write(root, command, defaultValues);
                    }

                    return(ex.ExitCode);
                }
            }
            // To prevent the app from showing the annoying Windows troubleshooting dialog,
            // we handle all exceptions and route them to the console nicely.
            // However, we don't want to swallow unhandled exceptions when the debugger is attached,
            // because we still want the IDE to show them to the developer.
            catch (Exception ex) when(!Debugger.IsAttached)
            {
                WriteError(ex.ToString());
                return(ExitCode.FromException(ex));
            }
        }
Beispiel #10
0
        public async ValueTask <int> RunAsync(IEnumerable <string> commandLineArguments,
                                              IReadOnlyDictionary <string, string> environmentVariables)
        {
            try
            {
                _logger.LogInformation("Starting CLI application...");
                _console.ResetColor();

                _environmentVariablesAccessor.EnvironmentVariables = environmentVariables;

                RootSchema rootSchema = _rootSchemaAccessor.RootSchema; //Force root schema to resolve. TODO: find a solution to enable lazy root schema resolving.

                //TODO: OnStart()

                _startupMessage?.Invoke(_metadata, _console);

                int exitCode = await StartAppAsync(commandLineArguments);

                //TODO: OnStop()
                _logger.LogInformation("CLI application stopped.");

                return(exitCode);
            }
            // This may throw pre-execution resolving exceptions which are useful only to the end-user
            catch (TypinException ex)
            {
                _logger.LogDebug(ex, $"{nameof(TypinException)} occured. Trying to find exception handler.");

                IEnumerable <ICliExceptionHandler> exceptionHandlers = _serviceProvider.GetServices <ICliExceptionHandler>();
                foreach (ICliExceptionHandler handler in exceptionHandlers)
                {
                    if (handler.HandleException(ex))
                    {
                        _logger.LogDebug(ex, "Exception handled by {ExceptionHandlerType}.", handler.GetType().FullName);

                        break;
                    }
                }

                _logger.LogCritical(ex, "Unhandled Typin exception caused app to terminate.");

                _console.Error.WithForegroundColor(ConsoleColor.DarkRed, (error) => error.WriteLine($"Unhandled Typin exception caused app to terminate."));
                _console.Error.WriteLine();
                _console.Error.WriteException(ex);

                return(ExitCodes.FromException(ex));
            }
            // To prevent the app from showing the annoying Windows troubleshooting dialog,
            // we handle all exceptions and route them to the console nicely.
            // However, we don't want to swallow unhandled exceptions when the debugger is attached,
            // because we still want the IDE to show them to the developer.
            catch (Exception ex) //when (!Debugger.IsAttached)
            {
                _logger.LogCritical(ex, "Unhandled exception caused app to terminate.");

                _console.Error.WithForegroundColor(ConsoleColor.DarkRed, (error) => error.WriteLine($"Fatal error occured in {_metadata.ExecutableName}."));
                _console.Error.WriteLine();
                _console.Error.WriteException(ex);

                return(ExitCodes.FromException(ex));
            }
        }
Beispiel #11
0
        public RootSchemaTests()
        {
            var serviceProvider = Mock.Of <IServiceProvider>();

            _rootSchema = new RootSchema(serviceProvider);
        }