Esempio n. 1
0
        /// <summary>
        /// Called to execute the code generator application.
        /// </summary>
        /// <param name="args">The command-line arguments.</param>
        /// <returns>The exit code.</returns>
        public int Run(IReadOnlyList <string> args)
        {
            try
            {
                var argsReader = new ArgsReader(args);
                if (argsReader.ReadHelpFlag())
                {
                    foreach (string line in Description)
                    {
                        System.Console.WriteLine(line);
                    }
                    System.Console.WriteLine();
                    WriteUsage();
                    return(0);
                }

                var generator = CreateGenerator(argsReader);
                generator.GeneratorName = s_assemblyName;
                if (SupportsCustomIndent)
                {
                    string indentText = argsReader.ReadIndentOption();
                    if (indentText != null)
                    {
                        generator.IndentText = indentText;
                    }
                }
                if (SupportsCustomNewLine)
                {
                    string newLine = argsReader.ReadNewLineOption();
                    if (newLine != null)
                    {
                        generator.NewLine = newLine;
                    }
                }

                string serviceName = argsReader.ReadServiceNameOption();
                bool   shouldClean = SupportsClean && argsReader.ReadCleanFlag();
                bool   isQuiet     = argsReader.ReadQuietFlag();
                bool   isVerify    = argsReader.ReadVerifyFlag();
                bool   isDryRun    = argsReader.ReadDryRunFlag();

                string inputPath = argsReader.ReadArgument();
                if (inputPath == null)
                {
                    throw new ArgsReaderException("Missing input path.");
                }

                string outputPath = argsReader.ReadArgument();
                if (outputPath == null)
                {
                    throw new ArgsReaderException("Missing output path.");
                }

                argsReader.VerifyComplete();

                NamedText input;
                if (inputPath == "-")
                {
                    input = new NamedText("", System.Console.In.ReadToEnd());
                }
                else
                {
                    if (!File.Exists(inputPath))
                    {
                        throw new ApplicationException("Input file does not exist: " + inputPath);
                    }
                    input = new NamedText(Path.GetFileName(inputPath), File.ReadAllText(inputPath));
                }

                ServiceInfo service;
                if (ServiceDefinitionUtility.DetectFormat(input) == ServiceDefinitionFormat.Swagger)
                {
                    service = new SwaggerParser {
                        ServiceName = serviceName
                    }.ParseDefinition(input);
                }
                else
                {
                    if (serviceName != null)
                    {
                        throw new ArgsReaderException("--serviceName not supported for FSD input.");
                    }
                    service = new FsdParser().ParseDefinition(input);
                }

                PrepareGenerator(generator, service, outputPath);
                var output = generator.GenerateOutput(service);

                if (SupportsSingleOutput &&
                    !outputPath.EndsWith("/", StringComparison.Ordinal) &&
                    !outputPath.EndsWith("\\", StringComparison.Ordinal) &&
                    !Directory.Exists(outputPath))
                {
                    if (output.NamedTexts.Count > 1)
                    {
                        throw new InvalidOperationException("Multiple outputs not expected.");
                    }

                    if (output.NamedTexts.Count == 1)
                    {
                        var namedText = output.NamedTexts[0];

                        if (outputPath == "-")
                        {
                            System.Console.Write(namedText.Text);
                        }
                        else if (ShouldWriteByteOrderMark(namedText.Name))
                        {
                            File.WriteAllText(outputPath, namedText.Text, s_utf8WithBom);
                        }
                        else
                        {
                            File.WriteAllText(outputPath, namedText.Text);
                        }
                    }
                }
                else
                {
                    var namedTextsToWrite = new List <NamedText>();
                    foreach (var namedText in output.NamedTexts)
                    {
                        string existingFilePath = Path.Combine(outputPath, namedText.Name);
                        if (File.Exists(existingFilePath))
                        {
                            // ignore CR when comparing files
                            if (namedText.Text.Replace("\r", "") != File.ReadAllText(existingFilePath).Replace("\r", ""))
                            {
                                namedTextsToWrite.Add(namedText);
                                if (!isQuiet)
                                {
                                    System.Console.WriteLine("changed " + namedText.Name);
                                }
                            }
                        }
                        else
                        {
                            namedTextsToWrite.Add(namedText);
                            if (!isQuiet)
                            {
                                System.Console.WriteLine("added " + namedText.Name);
                            }
                        }
                    }

                    var namesToDelete = new List <string>();
                    if (shouldClean && output.PatternsToClean.Count != 0)
                    {
                        var directoryInfo = new DirectoryInfo(outputPath);
                        if (directoryInfo.Exists)
                        {
                            foreach (string nameMatchingPattern in FindNamesMatchingPatterns(directoryInfo, output.PatternsToClean))
                            {
                                if (output.NamedTexts.All(x => x.Name != nameMatchingPattern))
                                {
                                    namesToDelete.Add(nameMatchingPattern);
                                    if (!isQuiet)
                                    {
                                        System.Console.WriteLine("removed " + nameMatchingPattern);
                                    }
                                }
                            }
                        }
                    }

                    if (isVerify)
                    {
                        return(namedTextsToWrite.Count != 0 || namesToDelete.Count != 0 ? 1 : 0);
                    }

                    if (!isDryRun)
                    {
                        if (!Directory.Exists(outputPath))
                        {
                            Directory.CreateDirectory(outputPath);
                        }

                        foreach (var namedText in namedTextsToWrite)
                        {
                            string outputFilePath = Path.Combine(outputPath, namedText.Name);

                            string outputFileDirectoryPath = Path.GetDirectoryName(outputFilePath);
                            if (outputFileDirectoryPath != null && outputFileDirectoryPath != outputPath && !Directory.Exists(outputFileDirectoryPath))
                            {
                                Directory.CreateDirectory(outputFileDirectoryPath);
                            }

                            if (ShouldWriteByteOrderMark(namedText.Name))
                            {
                                File.WriteAllText(outputFilePath, namedText.Text, s_utf8WithBom);
                            }
                            else
                            {
                                File.WriteAllText(outputFilePath, namedText.Text);
                            }
                        }

                        foreach (string nameToDelete in namesToDelete)
                        {
                            File.Delete(Path.Combine(outputPath, nameToDelete));
                        }
                    }
                }

                return(0);
            }
            catch (Exception exception)
            {
                if (exception is ApplicationException || exception is ArgsReaderException || exception is ServiceDefinitionException)
                {
                    System.Console.Error.WriteLine(exception.Message);
                    if (exception is ArgsReaderException)
                    {
                        System.Console.Error.WriteLine();
                        WriteUsage();
                    }
                    return(2);
                }
                else
                {
                    System.Console.Error.WriteLine(exception.ToString());
                    return(3);
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Parses Swagger (OpenAPI) 2.0 into a service definition.
        /// </summary>
        public ServiceInfo ParseDefinition(NamedText source)
        {
            if (string.IsNullOrWhiteSpace(source.Text))
            {
                throw new ServiceDefinitionException("Service definition is missing.", new NamedTextPosition(source.Name, 1, 1));
            }

            SwaggerService       swaggerService;
            SwaggerParserContext context;

            if (!s_detectJsonRegex.IsMatch(source.Text))
            {
                // parse YAML
                var yamlDeserializer = new DeserializerBuilder()
                                       .IgnoreUnmatchedProperties()
                                       .WithNamingConvention(new OurNamingConvention())
                                       .Build();
                using (var stringReader = new StringReader(source.Text))
                {
                    try
                    {
                        swaggerService = yamlDeserializer.Deserialize <SwaggerService>(stringReader);
                    }
                    catch (YamlException exception)
                    {
                        var          exceptionError  = exception.InnerException?.Message ?? exception.Message;
                        const string errorStart      = "): ";
                        int          errorStartIndex = exceptionError.IndexOf(errorStart, StringComparison.OrdinalIgnoreCase);
                        if (errorStartIndex != -1)
                        {
                            exceptionError = exceptionError.Substring(errorStartIndex + errorStart.Length);
                        }

                        var exceptionPosition = new NamedTextPosition(source.Name, exception.End.Line, exception.End.Column);
                        throw new ServiceDefinitionException(exceptionError, exceptionPosition);
                    }
                }
                if (swaggerService == null)
                {
                    throw new ServiceDefinitionException("Service definition is missing.", new NamedTextPosition(source.Name, 1, 1));
                }

                context = SwaggerParserContext.FromYaml(source);
            }
            else
            {
                // parse JSON
                using (var stringReader = new StringReader(source.Text))
                    using (var jsonTextReader = new JsonTextReader(stringReader))
                    {
                        try
                        {
                            swaggerService = JsonSerializer.Create(SwaggerUtility.JsonSerializerSettings).Deserialize <SwaggerService>(jsonTextReader);
                        }
                        catch (JsonException exception)
                        {
                            var exceptionPosition = new NamedTextPosition(source.Name, jsonTextReader.LineNumber, jsonTextReader.LinePosition);
                            throw new ServiceDefinitionException(exception.Message, exceptionPosition);
                        }

                        context = SwaggerParserContext.FromJson(source);
                    }
            }

            return(ConvertSwaggerService(swaggerService, context));
        }
        public async Task <ServiceResult <GenerateResponseDto> > GenerateAsync(GenerateRequestDto request, CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            try
            {
                var  input     = new NamedText(request.Definition?.Name ?? "", request.Definition?.Text ?? "");
                bool isSwagger = ServiceDefinitionUtility.DetectFormat(input) == ServiceDefinitionFormat.Swagger;
                var  service   = isSwagger ? new SwaggerParser().ParseDefinition(input) : new FsdParser().ParseDefinition(input);

                var generatorName = request.Generator?.Name;
                switch (generatorName)
                {
                case "csharp":
                    return(ServiceResult.Success(GenerateCode(() => new CSharpGenerator(), g => g.GenerateOutput(service))));

                case "javascript":
                    return(ServiceResult.Success(GenerateCode(() => new JavaScriptGenerator(), g => g.GenerateOutput(service))));

                case "typescript":
                    return(ServiceResult.Success(GenerateCode(() => new JavaScriptGenerator {
                        TypeScript = true
                    }, g => g.GenerateOutput(service))));

                case "markdown":
                    return(ServiceResult.Success(GenerateCode(() => new MarkdownGenerator(), g => g.GenerateOutput(service))));

                case "fsd":
                    return(ServiceResult.Success(GenerateCode(() => new FsdGenerator(), g => g.GenerateOutput(service))));

                case "swagger-json":
                    return(ServiceResult.Success(GenerateCode(() => new SwaggerGenerator(), g => g.GenerateOutput(service))));

                case "swagger-yaml":
                    return(ServiceResult.Success(GenerateCode(() => new SwaggerGenerator {
                        Yaml = true
                    }, g => g.GenerateOutput(service))));

                case "asp-net-web-api":
                    return(ServiceResult.Success(GenerateCode(() => new AspNetGenerator(), g => g.GenerateOutput(service))));

                case "crash":
                    throw new InvalidOperationException("Intentional exception for diagnostic purposes.");

                default:
                    return(ServiceResult.Failure(ServiceErrors.CreateInvalidRequest($"Unrecognized generator '{generatorName}'.")));
                }
            }
            catch (ServiceDefinitionException exception)
            {
                return(ServiceResult.Success(new GenerateResponseDto
                {
                    Failure = new FailureDto
                    {
                        Message = exception.Error,
                        Line = exception.Position.LineNumber,
                        Column = exception.Position.ColumnNumber,
                    },
                }));
            }
        }
Esempio n. 4
0
 public static SwaggerParserContext FromJson(NamedText namedText)
 {
     return(new SwaggerParserContext(namedText, isYaml: false));
 }
Esempio n. 5
0
 private SwaggerParserContext(NamedText namedText, bool isYaml, string path = null)
 {
     m_namedText = namedText;
     m_isYaml    = isYaml;
     m_path      = path;
 }
Esempio n. 6
0
 public static SwaggerParserContext FromYaml(NamedText namedText)
 {
     return(new SwaggerParserContext(namedText, isYaml: true));
 }
Esempio n. 7
0
 public Context(NamedText source, IReadOnlyDictionary <string, FsdRemarksSection> remarksSections)
 {
     m_source          = source;
     m_remarksSections = remarksSections;
 }
Esempio n. 8
0
 public static ServiceInfo ParseDefinition(NamedText source, IReadOnlyDictionary <string, FsdRemarksSection> remarksSections)
 {
     return(DefinitionParser(new Context(source, remarksSections)).Parse(source.Text));
 }
Esempio n. 9
0
        /// <summary>
        /// Parses an FSD file into a service definition.
        /// </summary>
        public ServiceInfo ParseDefinition(NamedText source)
        {
            IReadOnlyList <string> definitionLines = null;
            var remarksSections = new Dictionary <string, FsdRemarksSection>(StringComparer.OrdinalIgnoreCase);

            // read remarks after definition
            using (var reader = new StringReader(source.Text))
            {
                string name              = null;
                var    lines             = new List <string>();
                int    lineNumber        = 0;
                int    headingLineNumber = 0;

                while (true)
                {
                    string line = reader.ReadLine();
                    lineNumber++;

                    Match match = line == null ? null : s_markdownHeading.Match(line);
                    if (match == null || match.Success)
                    {
                        if (name == null)
                        {
                            definitionLines = lines;
                        }
                        else
                        {
                            while (lines.Count != 0 && string.IsNullOrWhiteSpace(lines[0]))
                            {
                                lines.RemoveAt(0);
                            }
                            while (lines.Count != 0 && string.IsNullOrWhiteSpace(lines[lines.Count - 1]))
                            {
                                lines.RemoveAt(lines.Count - 1);
                            }

                            var position = new NamedTextPosition(source.Name, headingLineNumber, 1);
                            if (remarksSections.ContainsKey(name))
                            {
                                throw new ServiceDefinitionException("Duplicate remarks heading: " + name, position);
                            }
                            remarksSections.Add(name, new FsdRemarksSection(name, lines, position));
                        }

                        if (match == null)
                        {
                            break;
                        }

                        name              = line.Substring(match.Index + match.Length).Trim();
                        lines             = new List <string>();
                        headingLineNumber = lineNumber;
                    }
                    else
                    {
                        lines.Add(line);
                    }
                }
            }

            source = new NamedText(source.Name, string.Join("\n", definitionLines));

            ServiceInfo service;

            try
            {
                service = FsdParsers.ParseDefinition(source, remarksSections);
            }
            catch (ParseException exception)
            {
                var expectation = exception
                                  .Result
                                  .GetNamedFailures()
                                  .Distinct()
                                  .GroupBy(x => x.Position)
                                  .Select(x => new { LineColumn = x.Key.GetLineColumn(), Names = x.Select(y => y.Name) })
                                  .OrderByDescending(x => x.LineColumn.LineNumber)
                                  .ThenByDescending(x => x.LineColumn.ColumnNumber)
                                  .First();

                throw new ServiceDefinitionException(
                          "expected " + string.Join(" or ", expectation.Names.Distinct().OrderBy(GetExpectationNameRank).ThenBy(x => x, StringComparer.Ordinal)),
                          new NamedTextPosition(source.Name, expectation.LineColumn.LineNumber, expectation.LineColumn.ColumnNumber),
                          exception);
            }

            // check for unused remarks sections
            foreach (var remarksSection in remarksSections.Values)
            {
                string sectionName = remarksSection.Name;
                if (service.Name != sectionName && service.FindMember(sectionName) == null)
                {
                    throw new ServiceDefinitionException($"Unused remarks heading: {sectionName}", remarksSection.Position);
                }
            }

            return(service);
        }
Esempio n. 10
0
 /// <summary>
 /// Creates a single-output instance.
 /// </summary>
 public CodeGenOutput(NamedText namedText)
     : this(namedTexts : namedText == null ? null : new[] { namedText }, patternsToClean : null)
 {
 }