/// <summary>
        /// Convert a tablespec block into one of our internal object model representations
        /// </summary>
        public TableDefinition ParseTableSpec(Block tableSpecBlock, Stack <Config.DocumentHeader> headerStack, IssueLogger issues)
        {
            List <ValidationError> discoveredErrors = new List <ValidationError>();
            List <ItemDefinition>  items            = new List <ItemDefinition>();

            var tableShape = tableSpecBlock.Table;

            TableDecoder decoder = new TableDecoder {
                Type = TableBlockType.Unknown
            };

            var headerText = headerStack.Peek()?.Title;

            // Try matching based on header
            if (headerText != null)
            {
                var matchingDecoder = FindDecoderFromHeaderText(headerStack);
                if (null != matchingDecoder)
                {
                    decoder = matchingDecoder;
                }
            }

            // Try matching based on shape
            if (decoder.Type == TableBlockType.Unknown && null != tableSpecBlock.Table)
            {
                var matchingDecoder = FindDecoderFromShape(tableShape);
                if (null != matchingDecoder)
                {
                    decoder = matchingDecoder;
                }
            }

            switch (decoder.Type)
            {
            case TableBlockType.ErrorCodes:
                items.AddRange(ParseErrorTable(tableShape, decoder));
                break;

            case TableBlockType.PathParameters:
                items.AddRange(ParseParameterTable(tableShape, ParameterLocation.Path, decoder, issues.For($"{decoder.Type}Table")));
                break;

            case TableBlockType.ResourcePropertyDescriptions:
            case TableBlockType.RequestObjectProperties:
            case TableBlockType.ResponseObjectProperties:
                items.AddRange(ParseParameterTable(tableShape, ParameterLocation.JsonObject, decoder, issues));
                break;

            case TableBlockType.ResourceNavigationPropertyDescriptions:
                items.AddRange(ParseParameterTable(tableShape, ParameterLocation.JsonObject, decoder, issues, true));
                break;

            case TableBlockType.HttpHeaders:
                items.AddRange(ParseParameterTable(tableShape, ParameterLocation.Header, decoder, issues));
                break;

            case TableBlockType.QueryStringParameters:
                items.AddRange(ParseParameterTable(tableShape, ParameterLocation.QueryString, decoder, issues));
                break;

            case TableBlockType.EnumerationValues:
                items.AddRange(ParseEnumerationTable(tableShape, decoder));
                break;

            case TableBlockType.AuthScopes:
                items.AddRange(ParseAuthScopeTable(tableShape, decoder));
                break;

            case TableBlockType.Unknown:
                var headers = tableShape.ColumnHeaders != null?tableShape.ColumnHeaders.ComponentsJoinedByString(",") : "null";

                issues.Message($"Ignored unclassified table: headerText='{headerText}', tableHeaders='{headers}'");
                break;

            default:
                var hdrs = tableShape.ColumnHeaders != null?tableShape.ColumnHeaders.ComponentsJoinedByString(",") : "null";

                issues.Message($"Ignored table: classification='{decoder.Type}', headerText='{headerText}', tableHeaders='{hdrs}'");
                break;
            }

            return(new TableDefinition(decoder.Type, items, headerText));
        }
        private static IEnumerable <AuthScopeDefinition> ParseAuthScopeTable(IMarkdownTable table, TableDecoder decoder)
        {
            var records = from r in table.RowValues
                          select new AuthScopeDefinition
            {
                Scope       = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["scope"]),
                Title       = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["title"]),
                Description = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"]),
                Required    = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["required"]).ToBoolean()
            };

            return(records);
        }
        private static IEnumerable <EnumerationDefinition> ParseEnumerationTable(IMarkdownTable table, TableDecoder decoder)
        {
            List <EnumerationDefinition> records = new List <EnumerationDefinition>();

            foreach (var r in table.RowValues)
            {
                var usedColumns = new List <string>();
                records.Add(new EnumerationDefinition
                {
                    MemberName   = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["memberName"], usedColumns),
                    NumericValue = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["numericValue"], usedColumns).ToInt32(),
                    Description  = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"], usedColumns),
                    TypeName     = decoder.Stamp,
                    IsFlags      = decoder.IsFlags,
                });
            }

            return(records);
        }
        private static IEnumerable <ParameterDefinition> ParseParameterTable(IMarkdownTable table, ParameterLocation location, TableDecoder decoder, IssueLogger issues, bool navigationProperties = false)
        {
            var records = table.RowValues.Select(r => new ParameterDefinition
            {
                Name          = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["name"]),
                Type          = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["type"]).ParseParameterDataType(defaultValue: ParameterDataType.String),
                Description   = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"]),
                Required      = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"]).IsRequired(),
                Optional      = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"]).IsOptional(),
                Location      = location,
                IsNavigatable = navigationProperties,
            }).ToList();

            var badRows = records.Count(r => string.IsNullOrEmpty(r.Name));

            if (badRows > 0)
            {
                var tableHeaders = $"|{ string.Join("|", table.ColumnHeaders)}|";
                if (badRows == records.Count)
                {
                    issues.Warning(ValidationErrorCode.MarkdownParserError, $"Failed to parse any rows out of table with headers: {tableHeaders}");
                    return(Enumerable.Empty <ParameterDefinition>());
                }

                issues.Warning(ValidationErrorCode.ParameterParserError, $"Failed to parse {badRows} row(s) in table with headers: {tableHeaders}");
                records = records.Where(r => !string.IsNullOrEmpty(r.Name)).ToList();
            }

            return(records);
        }
        /// <summary>
        /// Convert a markdown table into ErrorDefinition objects
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        private static IEnumerable <ErrorDefinition> ParseErrorTable(IMarkdownTable table, TableDecoder decoder)
        {
            var records = from r in table.RowValues
                          select new ErrorDefinition
            {
                HttpStatusCode    = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["httpStatusCode"]),
                HttpStatusMessage = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["httpStatusMessage"]),
                ErrorCode         = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["errorCode"]),
                Description       = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"])
            };

            return(records);
        }
示例#6
0
        private static IEnumerable <ParameterDefinition> ParseParameterTable(IMarkdownTable table, ParameterLocation location, TableDecoder decoder, IssueLogger issues, bool navigationProperties = false)
        {
            // tables sometimes have column spans to delineate different sections of the table. for instance:
            //
            // | Name | Type   | Description
            // |------|--------|--------------
            // | one  | int    | first number
            // | two  | int    | second number
            // | **fancy numbers**
            // | pi   | double | third number
            //
            // our markdown parser captures this as a regular row with all the columns, except with &nbsp; for all the blanks.
            // we try to infer such rows by looking for a **bold** first cell, followed by nbsp in all the other cells.
            // see below.

            var records = table.RowValues.
                          Where(r => !r[0].StartsWith("**") || r.Skip(1).Any(c => c != "&nbsp;")). // see comment above
                          Select(r => new ParameterDefinition
            {
                Name          = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["name"]),
                Type          = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["type"]).ParseParameterDataType(defaultValue: ParameterDataType.String),
                Description   = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"]),
                Required      = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"]).IsRequired(),
                Optional      = r.ValueForColumn(table, decoder.ParseRule.ColumnNames["description"]).IsOptional(),
                Location      = location,
                IsNavigatable = navigationProperties,
            }).ToList();

            var badRows = records.Count(r => string.IsNullOrEmpty(r.Name));

            if (badRows > 0)
            {
                var tableHeaders = $"|{ string.Join("|", table.ColumnHeaders)}|";
                if (badRows == records.Count)
                {
                    issues.Warning(ValidationErrorCode.MarkdownParserError, $"Failed to parse any rows out of table with headers: {tableHeaders}\nTable was parsed as {decoder.ParseAs}, which requires a name column called one of the following: {{ {String.Join(",", decoder.ParseRule.ColumnNames["name"])} }}");
                    return(Enumerable.Empty <ParameterDefinition>());
                }

                issues.Warning(ValidationErrorCode.ParameterParserError, $"Failed to parse {badRows} row(s) in table with headers: {tableHeaders}");
                records = records.Where(r => !string.IsNullOrEmpty(r.Name)).ToList();
            }

            return(records);
        }