refactor(dedup): adapter-based Markdown CollectionGenerator dedup (the big one)
SonarQube Analysis / sonarqube (pull_request) Successful in 3m42s
SonarQube Analysis / sonarqube (pull_request) Successful in 3m42s
Final cluster Sonar was reporting: the 82-line copy-paste between `Markdown.SqlServer.QueryBreakdownCollectionGenerator` and `Markdown.PostgreSql.QueryBreakdownCollectionGenerator`. Both classes existed because each dialect has a different concrete `QueryBreakdownCollection` type with its own `ParameterUsageReport` class — no shared base for the methods to operate on. Resolves it with an adapter pattern in `Markdown.Common`: - **`ICollectionMarkdownData`** (new, internal): dialect-neutral view exposing query count, parameter / column / table totals, queries- for-report list, and parameter-rows (already-mapped to the writer's `ParameterUsageRow` type). - **`CollectionMarkdownGenerator`** (new, internal static): single template that takes the data + `MarkdownDialectFormat` and routes through `CollectionReportWriter`. The six `GenerateX` methods that were duplicated three times now live here once. - **SqlServer / PostgreSql wrappers**: shrunk to a `Format` static, a thin one-line forwarder per public method, and a private sealed `Adapter : ICollectionMarkdownData` nested class that does the dialect-specific extraction (including the `ParameterUsageReport → ParameterUsageRow` mapping that was previously duplicated three times as `MapParameters`). Public API unchanged — the existing `Markdown.SqlServer.QueryBreakdownCollectionGenerator.GenerateCollectionReport(collection, title)` etc. continue to work as before; their bodies just delegate. The three dialect-specific `ParameterUsageReport` classes are deliberately *not* unified yet — their `ToString()` overrides differ meaningfully per dialect and unifying would be a separate API discussion. Snowflake wrapper not touched in this commit — Sonar didn't flag it (its `GenerateSnowflakeFeaturesAnalysis` and feature-aware `QueryCompositionReport` callback make it structurally distinct). Consistency follow-up could move it onto the same adapter pattern without behavior change. All 1180 tests stay green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
089d4f6000
commit
4038b3dab5
@@ -0,0 +1,37 @@
|
||||
namespace Strata.SqlTools.Markdown.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Shared method bodies for the per-dialect <c>QueryBreakdownCollectionGenerator</c>
|
||||
/// classes. Each dialect wraps its <c>QueryBreakdownCollection</c> in an
|
||||
/// <see cref="ICollectionMarkdownData"/> adapter and supplies a dialect-specific
|
||||
/// <see cref="MarkdownDialectFormat"/>; this template handles the rest.
|
||||
/// </summary>
|
||||
internal static class CollectionMarkdownGenerator
|
||||
{
|
||||
public static string GenerateCollectionReport(ICollectionMarkdownData data, MarkdownDialectFormat format, string? title)
|
||||
=> CollectionReportWriter.CollectionReport(title, new[]
|
||||
{
|
||||
GenerateCollectionSummary(data),
|
||||
GenerateParameterAnalysis(data, format),
|
||||
GenerateQueryCompositionReport(data, format)
|
||||
});
|
||||
|
||||
public static string GenerateCollectionSummary(ICollectionMarkdownData data)
|
||||
=> CollectionReportWriter.CollectionSummary(
|
||||
data.QueryCount,
|
||||
data.UniqueParameterCount,
|
||||
data.TotalSelectedColumns,
|
||||
data.UniqueTableCount);
|
||||
|
||||
public static string GenerateParameterAnalysis(ICollectionMarkdownData data, MarkdownDialectFormat format)
|
||||
=> CollectionReportWriter.ParameterAnalysis(data.ParameterRows.ToList(), data.QueriesForReport, format);
|
||||
|
||||
public static string GenerateParameterDependencyDiagram(ICollectionMarkdownData data, MarkdownDialectFormat format)
|
||||
=> CollectionReportWriter.ParameterDependencyDiagram(data.QueriesForReport, format);
|
||||
|
||||
public static string GenerateQueryCompositionReport(ICollectionMarkdownData data, MarkdownDialectFormat format)
|
||||
=> CollectionReportWriter.QueryCompositionReport(data.QueriesForReport, format);
|
||||
|
||||
public static string GenerateBatchFlowDiagram(ICollectionMarkdownData data, string? openLabel, string? closeLabel)
|
||||
=> CollectionReportWriter.BatchFlowDiagram(data.QueryCount, openLabel, closeLabel);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using SqlServerBreakdowns = Strata.SqlTools.Breakdowns.SqlServer;
|
||||
|
||||
namespace Strata.SqlTools.Markdown.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Dialect-neutral view of a <c>QueryBreakdownCollection</c> as seen by the Markdown
|
||||
/// generators. Each per-dialect <c>QueryBreakdownCollectionGenerator</c> wraps its
|
||||
/// dialect-specific collection in an implementation of this interface so the shared
|
||||
/// <see cref="CollectionMarkdownGenerator"/> template can operate uniformly.
|
||||
/// </summary>
|
||||
internal interface ICollectionMarkdownData
|
||||
{
|
||||
int QueryCount { get; }
|
||||
int UniqueParameterCount { get; }
|
||||
int TotalSelectedColumns { get; }
|
||||
int UniqueTableCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Queries as the base <see cref="SqlServerBreakdowns.QueryBreakdown"/> type. Used
|
||||
/// directly by <see cref="CollectionReportWriter.QueryCompositionReport"/>
|
||||
/// (which needs <see cref="IReadOnlyList{T}"/>) and via covariance by the other
|
||||
/// writer methods that accept <see cref="IEnumerable{T}"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<SqlServerBreakdowns.QueryBreakdown> QueriesForReport { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameter usage already mapped into the writer's dialect-agnostic row type.
|
||||
/// </summary>
|
||||
IEnumerable<ParameterUsageRow> ParameterRows { get; }
|
||||
}
|
||||
@@ -23,79 +23,63 @@ public static class QueryBreakdownCollectionGenerator
|
||||
/// <summary>
|
||||
/// Generates a comprehensive collection report in Markdown format with PostgreSQL-specific information.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to document.</param>
|
||||
/// <param name="title">Optional title for the report.</param>
|
||||
/// <returns>A string containing the Markdown documentation.</returns>
|
||||
public static string GenerateCollectionReport(QueryBreakdownCollection collection, string? title = null)
|
||||
=> CollectionReportWriter.CollectionReport(title, new[]
|
||||
{
|
||||
GenerateCollectionSummary(collection),
|
||||
GenerateParameterAnalysis(collection),
|
||||
GenerateQueryCompositionReport(collection)
|
||||
});
|
||||
=> CollectionMarkdownGenerator.GenerateCollectionReport(Adapt(collection), Format, title);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a summary section for the collection.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to summarize.</param>
|
||||
/// <returns>Markdown summary section.</returns>
|
||||
public static string GenerateCollectionSummary(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.CollectionSummary(
|
||||
collection.QueryBreakdowns.Count,
|
||||
collection.GetAllUniqueParameters().Count(),
|
||||
collection.GetTotalSelectedColumns(),
|
||||
collection.GetUniqueTableReferences().Count());
|
||||
=> CollectionMarkdownGenerator.GenerateCollectionSummary(Adapt(collection));
|
||||
|
||||
/// <summary>
|
||||
/// Generates a parameter analysis report with PostgreSQL parameter syntax support.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to analyze.</param>
|
||||
/// <returns>Markdown parameter analysis section.</returns>
|
||||
public static string GenerateParameterAnalysis(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.ParameterAnalysis(MapParameters(collection), collection.QueryBreakdowns, Format);
|
||||
=> CollectionMarkdownGenerator.GenerateParameterAnalysis(Adapt(collection), Format);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a Mermaid diagram showing parameter dependencies across queries.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to visualize.</param>
|
||||
/// <returns>Mermaid diagram markdown.</returns>
|
||||
public static string GenerateParameterDependencyDiagram(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.ParameterDependencyDiagram(collection.QueryBreakdowns, Format);
|
||||
=> CollectionMarkdownGenerator.GenerateParameterDependencyDiagram(Adapt(collection), Format);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a detailed query composition report.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to report on.</param>
|
||||
/// <returns>Markdown composition report section.</returns>
|
||||
public static string GenerateQueryCompositionReport(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.QueryCompositionReport(collection.QueryBreakdowns, Format);
|
||||
=> CollectionMarkdownGenerator.GenerateQueryCompositionReport(Adapt(collection), Format);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a batch execution flow diagram for PostgreSQL.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to visualize.</param>
|
||||
/// <param name="includeTransaction">Whether to show transaction wrapping.</param>
|
||||
/// <returns>Mermaid diagram markdown.</returns>
|
||||
public static string GenerateBatchFlowDiagram(QueryBreakdownCollection collection, bool includeTransaction = false)
|
||||
=> CollectionReportWriter.BatchFlowDiagram(
|
||||
collection.QueryBreakdowns.Count,
|
||||
=> CollectionMarkdownGenerator.GenerateBatchFlowDiagram(
|
||||
Adapt(collection),
|
||||
includeTransaction ? "BEGIN" : null,
|
||||
includeTransaction ? "COMMIT" : null);
|
||||
|
||||
/// <summary>
|
||||
/// Maps the collection's parameter usage report into the writer's dialect-agnostic rows.
|
||||
/// </summary>
|
||||
private static List<ParameterUsageRow> MapParameters(QueryBreakdownCollection collection)
|
||||
=> collection.GetParameterUsageReport()
|
||||
.Select(p => new ParameterUsageRow
|
||||
private static ICollectionMarkdownData Adapt(QueryBreakdownCollection collection) => new Adapter(collection);
|
||||
|
||||
private sealed class Adapter : ICollectionMarkdownData
|
||||
{
|
||||
private readonly QueryBreakdownCollection _c;
|
||||
public Adapter(QueryBreakdownCollection c) { _c = c; }
|
||||
public int QueryCount => _c.QueryBreakdowns.Count;
|
||||
public int UniqueParameterCount => _c.GetAllUniqueParameters().Count();
|
||||
public int TotalSelectedColumns => _c.GetTotalSelectedColumns();
|
||||
public int UniqueTableCount => _c.GetUniqueTableReferences().Count();
|
||||
public IReadOnlyList<Breakdowns.SqlServer.QueryBreakdown> QueriesForReport => _c.QueryBreakdowns;
|
||||
public IEnumerable<ParameterUsageRow> ParameterRows
|
||||
=> _c.GetParameterUsageReport().Select(p => new ParameterUsageRow
|
||||
{
|
||||
ParameterName = p.ParameterName,
|
||||
IsUsedInAllQueries = p.IsUsedInAllQueries,
|
||||
UsedInQueryCount = p.UsedInQueryCount,
|
||||
TotalQueries = p.TotalQueries,
|
||||
Value = p.Value
|
||||
})
|
||||
.ToList();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a parameter using PostgreSQL syntax: <c>$n</c> for positional, <c>:name</c> for named.
|
||||
|
||||
@@ -22,79 +22,63 @@ public static class QueryBreakdownCollectionGenerator
|
||||
/// <summary>
|
||||
/// Generates a comprehensive collection report in Markdown format.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to document.</param>
|
||||
/// <param name="title">Optional title for the report.</param>
|
||||
/// <returns>A string containing the Markdown documentation.</returns>
|
||||
public static string GenerateCollectionReport(QueryBreakdownCollection collection, string? title = null)
|
||||
=> CollectionReportWriter.CollectionReport(title, new[]
|
||||
{
|
||||
GenerateCollectionSummary(collection),
|
||||
GenerateParameterAnalysis(collection),
|
||||
GenerateQueryCompositionReport(collection)
|
||||
});
|
||||
=> CollectionMarkdownGenerator.GenerateCollectionReport(Adapt(collection), Format, title);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a summary section for the collection.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to summarize.</param>
|
||||
/// <returns>Markdown summary section.</returns>
|
||||
public static string GenerateCollectionSummary(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.CollectionSummary(
|
||||
collection.QueryBreakdowns.Count,
|
||||
collection.GetAllUniqueParameters().Count(),
|
||||
collection.GetTotalSelectedColumns(),
|
||||
collection.GetUniqueTableReferences().Count());
|
||||
=> CollectionMarkdownGenerator.GenerateCollectionSummary(Adapt(collection));
|
||||
|
||||
/// <summary>
|
||||
/// Generates a parameter analysis report.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to analyze.</param>
|
||||
/// <returns>Markdown parameter analysis section.</returns>
|
||||
public static string GenerateParameterAnalysis(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.ParameterAnalysis(MapParameters(collection), collection.QueryBreakdowns, Format);
|
||||
=> CollectionMarkdownGenerator.GenerateParameterAnalysis(Adapt(collection), Format);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a Mermaid diagram showing parameter dependencies across queries.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to visualize.</param>
|
||||
/// <returns>Mermaid diagram markdown.</returns>
|
||||
public static string GenerateParameterDependencyDiagram(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.ParameterDependencyDiagram(collection.QueryBreakdowns, Format);
|
||||
=> CollectionMarkdownGenerator.GenerateParameterDependencyDiagram(Adapt(collection), Format);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a detailed query composition report.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to report on.</param>
|
||||
/// <returns>Markdown composition report section.</returns>
|
||||
public static string GenerateQueryCompositionReport(QueryBreakdownCollection collection)
|
||||
=> CollectionReportWriter.QueryCompositionReport(collection.QueryBreakdowns, Format);
|
||||
=> CollectionMarkdownGenerator.GenerateQueryCompositionReport(Adapt(collection), Format);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a batch execution flow diagram.
|
||||
/// </summary>
|
||||
/// <param name="collection">The QueryBreakdownCollection to visualize.</param>
|
||||
/// <param name="includeTransaction">Whether to show transaction wrapping.</param>
|
||||
/// <returns>Mermaid diagram markdown.</returns>
|
||||
public static string GenerateBatchFlowDiagram(QueryBreakdownCollection collection, bool includeTransaction = false)
|
||||
=> CollectionReportWriter.BatchFlowDiagram(
|
||||
collection.QueryBreakdowns.Count,
|
||||
=> CollectionMarkdownGenerator.GenerateBatchFlowDiagram(
|
||||
Adapt(collection),
|
||||
includeTransaction ? "BEGIN TRANSACTION" : null,
|
||||
includeTransaction ? "COMMIT TRANSACTION" : null);
|
||||
|
||||
/// <summary>
|
||||
/// Maps the collection's parameter usage report into the writer's dialect-agnostic rows.
|
||||
/// </summary>
|
||||
private static List<ParameterUsageRow> MapParameters(QueryBreakdownCollection collection)
|
||||
=> collection.GetParameterUsageReport()
|
||||
.Select(p => new ParameterUsageRow
|
||||
private static ICollectionMarkdownData Adapt(QueryBreakdownCollection collection) => new Adapter(collection);
|
||||
|
||||
private sealed class Adapter : ICollectionMarkdownData
|
||||
{
|
||||
private readonly QueryBreakdownCollection _c;
|
||||
public Adapter(QueryBreakdownCollection c) { _c = c; }
|
||||
public int QueryCount => _c.QueryBreakdowns.Count;
|
||||
public int UniqueParameterCount => _c.GetAllUniqueParameters().Count();
|
||||
public int TotalSelectedColumns => _c.GetTotalSelectedColumns();
|
||||
public int UniqueTableCount => _c.GetUniqueTableReferences().Count();
|
||||
public IReadOnlyList<QueryBreakdown> QueriesForReport => _c.QueryBreakdowns;
|
||||
public IEnumerable<ParameterUsageRow> ParameterRows
|
||||
=> _c.GetParameterUsageReport().Select(p => new ParameterUsageRow
|
||||
{
|
||||
ParameterName = p.ParameterName,
|
||||
IsUsedInAllQueries = p.IsUsedInAllQueries,
|
||||
UsedInQueryCount = p.UsedInQueryCount,
|
||||
TotalQueries = p.TotalQueries,
|
||||
Value = p.Value
|
||||
})
|
||||
.ToList();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter type name from a parameter value.
|
||||
|
||||
Reference in New Issue
Block a user