Skip to content

Extract GenerationOrchestrator from GenerateCommand#1128

Merged
christianhelle merged 2 commits into
mainfrom
refactor-generate-command
Jun 13, 2026
Merged

Extract GenerationOrchestrator from GenerateCommand#1128
christianhelle merged 2 commits into
mainfrom
refactor-generate-command

Conversation

@christianhelle

@christianhelle christianhelle commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Closes #1117

Extract core orchestration logic into a dedicated GenerationOrchestrator
class with IGenerationOrchestrator interface. GenerateCommand.ExecuteAsync
now delegates to the orchestrator, reducing from ~130 lines to ~30 lines.

  • Create IGenerationOrchestrator interface with RunAsync() method
  • Create GenerationOrchestrator implementing the full generation pipeline
  • Make IGenerationReporter and IMultiFileOutputReport public for wider use
  • Remove WriteSingleFile, WriteMultipleFiles, FormatFileSize, ShowWarnings, ValidateOpenApiSpec from GenerateCommand (moved to orchestrator)

Summary by CodeRabbit

Release Notes

  • New Features

    • Made IGenerationReporter and IMultiFileOutputReport public interfaces for library consumers who need to extend or customize code generation reporting.
  • Refactor

    • Restructured code generation workflow to use a dedicated orchestrator pattern for improved maintainability and reliability.
  • Tests

    • Added comprehensive integration tests for end-to-end code generation scenarios.

Extract core orchestration logic into a dedicated GenerationOrchestrator
class with IGenerationOrchestrator interface. GenerateCommand.ExecuteAsync
now delegates to the orchestrator, reducing from ~130 lines to ~30 lines.

- Create IGenerationOrchestrator interface with RunAsync() method
- Create GenerationOrchestrator implementing the full generation pipeline
- Make IGenerationReporter and IMultiFileOutputReport public for wider use
- Remove WriteSingleFile, WriteMultipleFiles, FormatFileSize, ShowWarnings,
  ValidateOpenApiSpec from GenerateCommand (moved to orchestrator)
- All 1958 existing tests pass without modification
- Test successful single-file generation
- Test successful multi-file generation
- Test error handling returns non-zero exit code
@christianhelle christianhelle self-assigned this Jun 13, 2026
@christianhelle christianhelle added the .NET Pull requests that contain changes to .NET code label Jun 13, 2026
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extracts the orchestration logic from GenerateCommand into a new GenerationOrchestrator module. The command becomes a thin CLI adapter that parses arguments and delegates the entire generation workflow to the orchestrator, which owns validation, file writing, analytics, error handling, and reporting. The orchestrator is backed by a public interface and integration tests.

Changes

Orchestrator Extraction and Refactoring

Layer / File(s) Summary
Orchestrator contract and public seams
src/Refitter/IGenerationOrchestrator.cs, src/Refitter/IGenerationReporter.cs
IGenerationOrchestrator interface defines RunAsync(RefitGeneratorSettings, Settings, IGenerationReporter, CancellationToken) returning Task<int>. IGenerationReporter and IMultiFileOutputReport promoted from internal to public to serve as orchestrator seams.
Orchestrator implementation
src/Refitter/GenerationOrchestrator.cs
GenerationOrchestrator.RunAsync orchestrates stopwatch/version/support reporting, spec-path resolution, generator creation, conditional OpenAPI validation, single or multi-file writing, analytics and settings output, success/warning reporting, banner display, and exception handling with diagnostics, error analytics, and exception HResult return.
GenerateCommand refactoring to thin adapter
src/Refitter/GenerateCommand.cs
ExecuteAsync now creates a reporter, handles --version, selects RefitGeneratorSettings from cache/settings-file/CLI, and delegates to GenerationOrchestrator.RunAsync. Prior inline generation, validation, writing, and error-reporting logic removed. Output-path helpers reorganized and settings caching simplified.
GenerationOrchestrator integration tests
src/Refitter.Tests/GenerationOrchestratorTests.cs
Three tests exercise GenerationOrchestrator.RunAsync: single-file generation with namespace/operation-id verification, multiple-file generation with output folder verification, and non-zero exit code on missing OpenAPI spec. All use temporary workspaces with cleanup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • christianhelle/refitter#1095: Both PRs refactor GenerateCommand's generation pipeline through modular seams—Refactor GenerateCommand into focused modules and stabilize package-reference builds #1095 via IGenerationReporter/RefitterSettingsLoader and this PR via new GenerationOrchestrator and delegation—with overlapping changes to the command workflow and spec-path resolution.
  • christianhelle/refitter#1000: Both PRs modify output-path logic in GenerateCommand (e.g., GetOutputPath, ApplySettingsFileDefaults)—this PR refactors those helpers, while #1000 hardens .refitter deserialization and output defaulting under ./Generated.
  • christianhelle/refitter#907: This PR's new GenerationOrchestrator centralizes OpenAPI validation/statistics workflow via OpenApiValidator/OpenApiStats, directly overlapping with #907's updates to Microsoft.OpenApi 3.x and interface-based visitor types.

Suggested labels

enhancement

Poem

🐰 From thick commands, we extract the soul,
An orchestrator takes the central role,
Testing now flows, no CLI parade,
Reusable logic, cleanly made.
Hop along, the workflow's now arranged!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Extract GenerationOrchestrator from GenerateCommand' accurately and specifically describes the primary change: extracting orchestration logic into a dedicated class.
Linked Issues check ✅ Passed The PR fulfills core requirements: GenerationOrchestrator with RunAsync created [#1117], GenerateCommand.ExecuteAsync reduced to ~30 lines (under 50) [#1117], IGenerationReporter/IMultiFileOutputReport made public for reuse [#1117], and unit tests for the orchestrator added [#1117].
Out of Scope Changes check ✅ Passed All changes are scoped to the extraction objective: new orchestrator implementation, refactored command, public interfaces, and orchestrator tests. No unrelated refactoring or feature additions detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor-generate-command

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/Refitter.Tests/GenerationOrchestratorTests.cs (1)

23-38: ⚡ Quick win

Extract the repeated OpenAPI payload into a shared test constant/helper.

The same inline spec shape is repeated across tests; centralizing it into constants/helpers aligns the suite pattern and reduces maintenance drift.

As per coding guidelines, "Follow the unit testing pattern with OpenAPI specification constants, [Fact] decorated test methods, FluentAssertions for validation, and BuildHelper.BuildCSharp() for compilation verification in test classes".

Also applies to: 88-104

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Refitter.Tests/GenerationOrchestratorTests.cs` around lines 23 - 38,
Extract the repeated OpenAPI JSON blob used in GenerationOrchestratorTests into
a shared constant or helper (e.g., add a public const string TestOpenApiSpec or
static readonly string OpenApiSpec in GenerationOrchestratorTests or a
TestConstants helper), then replace the inline triple-quoted payload passed to
File.WriteAllText(openApiPath, """...""") with that constant; update all other
occurrences of the same inline spec (the other span noted in the comment) to use
the new constant/helper so tests still call BuildHelper.BuildCSharp(), remain
decorated with [Fact], and validate with FluentAssertions without duplicating
the spec.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Refitter.Tests/GenerationOrchestratorTests.cs`:
- Around line 61-65: Tests in GenerationOrchestratorTests.cs only assert
generatedCode contains strings but do not verify it compiles; update the
success-path tests so that after reading generatedCode you call
BuildHelper.BuildCSharp(generatedCode) (or the overload used across tests) and
assert the build succeeded (e.g., no diagnostics/error and result is true) using
FluentAssertions. Modify the test cases that set and read the generatedCode
variable in the GenerationOrchestratorTests class (apply the same change to the
other success-path test mentioned) to include constants/OpenAPI spec usage as
needed and a final BuildHelper.BuildCSharp(...) call with an assertion to ensure
the emitted C# compiles.

In `@src/Refitter/GenerationOrchestrator.cs`:
- Around line 10-14: In RunAsync (GenerationOrchestrator.cs file lines 10-14)
ensure the incoming CancellationToken is enforced at stage boundaries by
invoking cancellationToken.ThrowIfCancellationRequested() (or equivalent checks)
before beginning each major generation phase inside RunAsync; in the same file
for the multi-file write loop (lines 125-141) add a cancellation check before
each file write iteration and pass the token into any async I/O calls used there
so writes can be aborted promptly; in GenerateCommand.cs (lines 67-68) propagate
the token by calling File.ReadAllTextAsync(..., cancellationToken) when loading
settings so the file read honors cancellation.

In `@src/Refitter/IGenerationOrchestrator.cs`:
- Around line 5-11: Add missing XML documentation for the newly public API
surfaces: in src/Refitter/IGenerationOrchestrator.cs (lines 5-11) add a summary
on the IGenerationOrchestrator interface and XML docs for the RunAsync method
describing its purpose, each parameter (settings: RefitGeneratorSettings,
cliSettings: Settings, reporter: IGenerationReporter, cancellationToken:
CancellationToken) and the Task<int> return value; in
src/Refitter/IGenerationReporter.cs (lines 14-65) add XML summary/comments for
the IGenerationReporter interface and every public member there explaining
intent, parameters and return values; in src/Refitter/IGenerationReporter.cs
(lines 72-77) add XML docs for the IMultiFileOutputReport members (summaries and
parameter/return descriptions); and in src/Refitter/GenerationOrchestrator.cs
(lines 8-14) add XML docs for the public GenerationOrchestrator class and its
RunAsync method mirroring the interface contract. Ensure tags use <summary>,
<param>, and <returns> consistently and accurately reference the method and
parameter names.

---

Nitpick comments:
In `@src/Refitter.Tests/GenerationOrchestratorTests.cs`:
- Around line 23-38: Extract the repeated OpenAPI JSON blob used in
GenerationOrchestratorTests into a shared constant or helper (e.g., add a public
const string TestOpenApiSpec or static readonly string OpenApiSpec in
GenerationOrchestratorTests or a TestConstants helper), then replace the inline
triple-quoted payload passed to File.WriteAllText(openApiPath, """...""") with
that constant; update all other occurrences of the same inline spec (the other
span noted in the comment) to use the new constant/helper so tests still call
BuildHelper.BuildCSharp(), remain decorated with [Fact], and validate with
FluentAssertions without duplicating the spec.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9db15cb9-369c-441d-a686-40b8534033fd

📥 Commits

Reviewing files that changed from the base of the PR and between bfb989b and 51f899c.

📒 Files selected for processing (5)
  • src/Refitter.Tests/GenerationOrchestratorTests.cs
  • src/Refitter/GenerateCommand.cs
  • src/Refitter/GenerationOrchestrator.cs
  • src/Refitter/IGenerationOrchestrator.cs
  • src/Refitter/IGenerationReporter.cs

Comment on lines +61 to +65
result.Should().Be(0);
File.Exists(outputPath).Should().BeTrue();
var generatedCode = await File.ReadAllTextAsync(outputPath);
generatedCode.Should().Contain("TestNamespace");
generatedCode.Should().Contain("GetPets");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add generated-code compilation checks in the success-path tests.

The tests validate text content but do not verify the generated C# compiles; this misses regressions that produce syntactically invalid output.

As per coding guidelines, "Follow the unit testing pattern with OpenAPI specification constants, [Fact] decorated test methods, FluentAssertions for validation, and BuildHelper.BuildCSharp() for compilation verification in test classes".

Also applies to: 129-134

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Refitter.Tests/GenerationOrchestratorTests.cs` around lines 61 - 65,
Tests in GenerationOrchestratorTests.cs only assert generatedCode contains
strings but do not verify it compiles; update the success-path tests so that
after reading generatedCode you call BuildHelper.BuildCSharp(generatedCode) (or
the overload used across tests) and assert the build succeeded (e.g., no
diagnostics/error and result is true) using FluentAssertions. Modify the test
cases that set and read the generatedCode variable in the
GenerationOrchestratorTests class (apply the same change to the other
success-path test mentioned) to include constants/OpenAPI spec usage as needed
and a final BuildHelper.BuildCSharp(...) call with an assertion to ensure the
emitted C# compiles.

Source: Coding guidelines

Comment on lines +10 to +14
public async Task<int> RunAsync(
RefitGeneratorSettings refitGeneratorSettings,
Settings cliSettings,
IGenerationReporter reporter,
CancellationToken cancellationToken)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cancellation token propagation is incomplete across the generation flow. The token is accepted at the API boundary but not consistently applied where work can block for meaningful durations.

  • src/Refitter/GenerationOrchestrator.cs#L10-L14: enforce cancellation checks at stage boundaries in RunAsync.
  • src/Refitter/GenerationOrchestrator.cs#L125-L141: check cancellation inside the multi-file write loop before each write.
  • src/Refitter/GenerateCommand.cs#L67-L68: pass cancellationToken to File.ReadAllTextAsync(...) when loading settings.
📍 Affects 2 files
  • src/Refitter/GenerationOrchestrator.cs#L10-L14 (this comment)
  • src/Refitter/GenerationOrchestrator.cs#L125-L141
  • src/Refitter/GenerateCommand.cs#L67-L68
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Refitter/GenerationOrchestrator.cs` around lines 10 - 14, In RunAsync
(GenerationOrchestrator.cs file lines 10-14) ensure the incoming
CancellationToken is enforced at stage boundaries by invoking
cancellationToken.ThrowIfCancellationRequested() (or equivalent checks) before
beginning each major generation phase inside RunAsync; in the same file for the
multi-file write loop (lines 125-141) add a cancellation check before each file
write iteration and pass the token into any async I/O calls used there so writes
can be aborted promptly; in GenerateCommand.cs (lines 67-68) propagate the token
by calling File.ReadAllTextAsync(..., cancellationToken) when loading settings
so the file read honors cancellation.

Comment on lines +5 to +11
public interface IGenerationOrchestrator
{
Task<int> RunAsync(
RefitGeneratorSettings settings,
Settings cliSettings,
IGenerationReporter reporter,
CancellationToken cancellationToken);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Public API expansion is missing full XML documentation across the new seams. This PR makes several types/members public, but the exposed API surface is not consistently documented.

  • src/Refitter/IGenerationOrchestrator.cs#L5-L11: add XML docs for the interface and RunAsync contract/parameters/return value.
  • src/Refitter/IGenerationReporter.cs#L14-L65: add XML docs for all public reporter members now exposed as API.
  • src/Refitter/IGenerationReporter.cs#L72-L77: add XML docs for IMultiFileOutputReport members.
  • src/Refitter/GenerationOrchestrator.cs#L8-L14: add XML docs for the public class and public RunAsync method.

As per coding guidelines, "Include XML documentation for public APIs in C# code".

📍 Affects 3 files
  • src/Refitter/IGenerationOrchestrator.cs#L5-L11 (this comment)
  • src/Refitter/IGenerationReporter.cs#L14-L65
  • src/Refitter/IGenerationReporter.cs#L72-L77
  • src/Refitter/GenerationOrchestrator.cs#L8-L14
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Refitter/IGenerationOrchestrator.cs` around lines 5 - 11, Add missing XML
documentation for the newly public API surfaces: in
src/Refitter/IGenerationOrchestrator.cs (lines 5-11) add a summary on the
IGenerationOrchestrator interface and XML docs for the RunAsync method
describing its purpose, each parameter (settings: RefitGeneratorSettings,
cliSettings: Settings, reporter: IGenerationReporter, cancellationToken:
CancellationToken) and the Task<int> return value; in
src/Refitter/IGenerationReporter.cs (lines 14-65) add XML summary/comments for
the IGenerationReporter interface and every public member there explaining
intent, parameters and return values; in src/Refitter/IGenerationReporter.cs
(lines 72-77) add XML docs for the IMultiFileOutputReport members (summaries and
parameter/return descriptions); and in src/Refitter/GenerationOrchestrator.cs
(lines 8-14) add XML docs for the public GenerationOrchestrator class and its
RunAsync method mirroring the interface contract. Ensure tags use <summary>,
<param>, and <returns> consistently and accurately reference the method and
parameter names.

Source: Coding guidelines

@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.78947% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.98%. Comparing base (469a4a0) to head (51f899c).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/Refitter/GenerationOrchestrator.cs 65.78% 29 Missing and 10 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1128      +/-   ##
==========================================
- Coverage   86.27%   83.98%   -2.30%     
==========================================
  Files          40       40              
  Lines        2551     2628      +77     
==========================================
+ Hits         2201     2207       +6     
- Misses        260      323      +63     
- Partials       90       98       +8     
Flag Coverage Δ
unittests 83.98% <65.78%> (-2.30%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Pull requests that contain changes to .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deepen GenerateCommand: Extract GenerationOrchestrator from thick CLI command

1 participant