fix(wrapper-generator): fail loudly on cmdlet file collisions - #3713
fix(wrapper-generator): fail loudly on cmdlet file collisions#3713Joywambui-maina wants to merge 2 commits into
Conversation
A second operation resolving to an already-written cmdlet file now fails generation with the full collision list instead of silently overwriting it, which is the silent-drop failure mode AutoRest had. OData cast list/item pairs (owners/graph.user) now merge like plain pairs, and the sweep's collisions land as cited NamingOverrides entries: termStore and agreement-file stitches, default-singleton renames (SubSite, DefaultDrive, DefaultCalendarEvent), and nested navs the SDK never shipped. Remaining families are tracked on #3704.
There was a problem hiding this comment.
Pull request overview
This PR hardens the WrapperGenerator to prevent silent cmdlet loss by detecting and failing on cmdlet file name collisions, and expands the naming-override data/model to encode published-SDK renames/suppressions (including broad “suffix” matching for recurring navigations). It also adjusts GET list/item pairing to merge OData cast list/item pairs into a single dispatcher cmdlet, and updates tests/docs accordingly.
Changes:
- Detect cmdlet
.g.csfile collisions during generation and fail with a consolidated, operation-identifying error message. - Extend
NamingOverridesto support exact/prefix/suffix path matching and add many oracle-/directive-cited rename/suppression entries to resolve known collisions. - Merge cast list/item GET pairs (e.g.,
.../owners/graph.userwith.../owners/{id}/graph.user) into one Get-* dispatcher; add regression/unit tests and documentation updates.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/WrapperGenerator/README.md | Updates documentation to reflect collision-guard behavior and expanded overrides/testing counts. |
| tools/WrapperGenerator/PowerShellWrapperGenerationService.cs | Tracks written files to detect collisions and throws a consolidated exception at end of generation. |
| tools/WrapperGenerator/NamingOverrides.cs | Reworks override matching to Exact/Prefix/Suffix and adds many new rename/suppression entries with citations. |
| tools/WrapperGenerator/edge-cases/naming-edge-cases.md | Documents collision families and how they’re handled/resolved. |
| tools/WrapperGenerator/CmdletNaming.cs | Enhances list/item merge detection to support OData cast list/item pairing. |
| tools/WrapperGenerator.Tests/NamingTests.cs | Adds unit coverage for new overrides and cast list/item pairing behavior. |
| tools/WrapperGenerator.Tests/GenerationServiceRegressionTests.cs | Adds regression test ensuring collisions fail loudly and identify both operations. |
| tools/Build-WrapperModule.ps1 | Improves failure capture to surface the generator exception text in build output summaries. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| @@ -298,6 +316,15 @@ private async Task<int> EmitGetOperationsAsync(List<GetOperationRecord> getOpera | |||
| private async Task<int> WriteCmdletFileAsync(CmdletNaming naming, string source, CancellationToken cancellationToken) | |||
| { | |||
| var fileName = naming.ClassName.Replace("Command", "", StringComparison.Ordinal) + ".g.cs"; | |||
| $lines = @($wrapperOut | ForEach-Object { "$_" }) | ||
| $exception = $lines | Where-Object { $_ -match 'Unhandled exception|Exception:' } | Select-Object -First 1 | ||
| $exceptionIndex = if ($exception) { $lines.IndexOf($exception) } else { -1 } |
… data Derive-CollisionResolutions.ps1 replays the checked-in collision inventory (212 lines, 365 contested routes) against MgCommandMetadata and emits exact-match resolution data: 191 suppressions (routes the published SDK prunes) and 64 renames (published nouns), each entry carrying its oracle evidence. The files embed into the generator and apply only when UseCollisionData is set; -Validate fails on drift, and a new xunit test runs it on every `dotnet test` so staleness fails the suite instead of depending on someone remembering to run the script by hand. Derivation itself fails on any unclassified or ambiguous route. Only 2 cross-path variant merges exist in all of v1.0 (GroupPhoto, ShareListItem) - deferred with the singleton side kept, cataloged in crosspath-merge-edge-cases.md. Full 39-module v1.0 generation now produces zero collisions; 20 published commands that lost filename races are recovered; exact-name matches rise 5,042 -> 5,098. Also: cmdlets emit into a per-module namespace derived from the client namespace instead of the leftover MgPoC placeholder; Build-WrapperModule's generated csproj references Authentication by a relative path instead of an absolute one; its -Configuration parameter now actually reaches the wrapper generator's own build, not just the final module build; and a pre-existing nullable warning in the list/item pairing check is fixed. 121 tests pass.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (2)
tools/Build-WrapperModule.ps1:152
- $lines is a PowerShell array; it does not reliably expose an instance IndexOf() method, so
$lines.IndexOf($exception)can throw when the wrapper generator fails (the exact path this new error-surfacing logic is meant to improve). Use[Array]::IndexOf($lines, $exception)(or a manual loop) to compute the index safely.
$lines = @($wrapperOut | ForEach-Object { "$_" })
$exception = $lines | Where-Object { $_ -match 'Unhandled exception|Exception:' } | Select-Object -First 1
$exceptionIndex = if ($exception) { $lines.IndexOf($exception) } else { -1 }
$result.Error = if ($exceptionIndex -ge 0) {
tools/WrapperGenerator/DerivedCollisionResolutions.cs:46
- Derived collision data lookup is currently API-version case-sensitive (
StringComparer.Ordinal). If a caller suppliesApiVersionwith different casing (e.g. "Beta" / "V1.0"), derived suppressions/renames will silently not apply and the generator may fail with avoidable collisions. Consider making the API-version dictionary case-insensitive (or normalizingApiVersion).
var result = new Dictionary<string, Tables>(StringComparer.Ordinal);
var assembly = typeof(DerivedCollisionResolutions).Assembly;
Changes proposed in this pull request
Other links