-
Notifications
You must be signed in to change notification settings - Fork 874
Added Code Examples links to class and interface pages. #4242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Conversation
| public class ExampleMetadataParser | ||
| { | ||
| private const string FragmentOutputDirectory = "ExampleFragments"; | ||
| private const string BaseCodeLibraryUrl = "https://docs.aws.amazon.com/code-library/latest/ug/dotnet_4"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens when these docs are deployed to alpha and integ doc sites, will this hardcoded url work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds code examples links to client and interface documentation pages by integrating with an example metadata JSON file (example_meta.json). The changes introduce a new parser to process example metadata, generate HTML fragments, and display them on the generated documentation pages. Additionally, a collapsible "In this article" table of contents is added to help users navigate page sections.
Key Changes:
- Created
ExampleMetadataParserto parse example metadata and generate HTML fragments for code examples - Enhanced
ClassWriterto include a page-level TOC and code examples section - Added JavaScript and CSS for collapsible TOC functionality
- Configured new command-line options for example metadata file path and error file
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| ExampleMetadataParser.cs | New parser that processes example_meta.json and generates HTML fragments for code examples per service |
| ClassWriter.cs | Added page TOC generation and code examples section integration |
| pagescript.js | Added toggleTOC function and scroll handler for collapsible TOC behavior |
| sdkstyle.css | Added CSS styles for collapsible page TOC component |
| GeneratorOptions.cs | Added properties for ExampleMetaJson and ExamplesErrorFile configuration |
| CommandLineParser.cs | Added command-line argument parsing for example metadata options |
| SdkDocGenerator.cs | Orchestrates example fragment generation and cleanup during doc build |
| doc-build.proj | Updated build configuration to pass example metadata parameters |
| example_meta.json | Sample metadata file with Control Tower examples (partial, injected by build system) |
| ExampleMetadataParserTests.cs | Unit tests for the new ExampleMetadataParser functionality |
| SDKDocGeneratorLib.csproj | Added System.Text.Json package dependency |
| // because any errors in the example_meta.json file must be corrected by the owners of | ||
| // the file. | ||
| Console.WriteLine($"Continuing without examples. Failed to generate example fragments: {ex.Message}"); | ||
| File.WriteAllText(examplesErrorFile, $"Failed to generate example fragments: {ex}"); |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the catch block, File.WriteAllText is called with examplesErrorFile, but if examplesErrorFile is null or empty (which can happen from the build configuration where ExamplesErrorFile defaults to empty string), this will throw an ArgumentException. Add validation to ensure examplesErrorFile is not null or empty before writing to it, or skip writing the error file if it's not specified.
| File.WriteAllText(examplesErrorFile, $"Failed to generate example fragments: {ex}"); | |
| if (!string.IsNullOrEmpty(examplesErrorFile)) | |
| { | |
| File.WriteAllText(examplesErrorFile, $"Failed to generate example fragments: {ex}"); | |
| } |
| // the file. | ||
| Console.WriteLine($"Continuing without examples. Failed to generate example fragments: {ex.Message}"); | ||
| File.WriteAllText(examplesErrorFile, $"Failed to generate example fragments: {ex}"); | ||
| CleanupExampleFragments(); |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ExampleFragmentsFullPath is set at the start of GenerateExampleFragments but is never reset to empty if an exception occurs. This means if GenerateExampleFragments is called multiple times (e.g., in tests or retry scenarios), CleanupExampleFragments() might attempt to delete a directory path from a previous failed call. Consider resetting ExampleFragmentsFullPath to string.Empty in the catch block after cleanup, or before setting it in the try block.
| CleanupExampleFragments(); | |
| CleanupExampleFragments(); | |
| ExampleFragmentsFullPath = string.Empty; |
|
|
||
|
|
||
| private static string SanitizeTitle(string title) | ||
| { |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SanitizeTitle method does not handle null input. If title is null, this will throw a NullReferenceException. Consider adding null checking to return null or an empty string for null inputs.
| { | |
| { | |
| if (title == null) | |
| return null; |
| function toggleTOC() { | ||
| var tocList = document.getElementById('tocList'); | ||
| var tocToggle = document.getElementById('tocToggle'); | ||
| if (tocList.style.display === 'none') { | ||
| tocList.style.display = 'block'; | ||
| tocToggle.innerHTML = '▼'; | ||
| } else { | ||
| tocList.style.display = 'none'; | ||
| tocToggle.innerHTML = '▶'; | ||
| } | ||
| } |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The toggleTOC function does not check if 'tocList' or 'tocToggle' elements exist before accessing them. If these elements are not present when the function is called, it will throw null reference errors. Add null checks before manipulating these elements.
| if (window.scrollY > 200) { | ||
| toc.classList.add('collapsed'); | ||
| document.getElementById('tocList').style.display = 'none'; | ||
| document.getElementById('tocToggle').innerHTML = '▶'; |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scroll event handler only adds the 'collapsed' class when scrolling down past 200px, but never removes it when scrolling back up. This means once the TOC is collapsed, it will remain in the collapsed/fixed state even if the user scrolls back to the top of the page. Consider adding logic to remove the 'collapsed' class when scrollY is less than or equal to 200.
| if (window.scrollY > 200) { | |
| toc.classList.add('collapsed'); | |
| document.getElementById('tocList').style.display = 'none'; | |
| document.getElementById('tocToggle').innerHTML = '▶'; | |
| if (!toc) { | |
| return; | |
| } | |
| if (window.scrollY > 200) { | |
| toc.classList.add('collapsed'); | |
| var tocList = document.getElementById('tocList'); | |
| var tocToggle = document.getElementById('tocToggle'); | |
| if (tocList) { | |
| tocList.style.display = 'none'; | |
| } | |
| if (tocToggle) { | |
| tocToggle.innerHTML = '▶'; | |
| } | |
| } else { | |
| toc.classList.remove('collapsed'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this valid? i think its saying once the user scrolls down, and then scrolls back up the TOC will not be visible?
| private static bool IsDotNetExample(Example example) | ||
| { | ||
|
|
||
| return example.Languages.Values.Any(lang => | ||
| lang.Name.Equals(".NET", StringComparison.OrdinalIgnoreCase) || | ||
| lang.Name.Equals("C#", StringComparison.OrdinalIgnoreCase) || | ||
| lang.Name.Equals("CSharp", StringComparison.OrdinalIgnoreCase)); | ||
| } |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The IsDotNetExample method does not handle cases where example.Languages or example.Languages.Values could be null. If example.Languages is null, calling Values will throw a NullReferenceException. Add a null check before accessing Languages.Values.
| public static string ToUpperFirstCharacter(string s) | ||
| { | ||
| var txt = s.Substring(0, 1).ToUpperInvariant(); | ||
| if (s.Length > 1) | ||
| txt += s.Substring(1); | ||
| return txt; | ||
| } |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ToUpperFirstCharacter method does not handle null or empty strings. If a null or empty string is passed, it will throw an ArgumentOutOfRangeException when calling s.Substring(0, 1). Add null/empty string validation before processing.
| var namespaceParts = this._versionType.Namespace.Split('.'); | ||
| var serviceId = namespaceParts[namespaceParts.Length - 1]; | ||
| return serviceId; |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GetServiceIdFromNamespace method does not handle cases where the namespace could be null or empty. If this._versionType.Namespace is null, this will throw a NullReferenceException. If it's empty, Split('.') will return an array with one empty element, but accessing the last element would still work. However, null should be handled explicitly.
| var namespaceParts = this._versionType.Namespace.Split('.'); | |
| var serviceId = namespaceParts[namespaceParts.Length - 1]; | |
| return serviceId; | |
| var namespaceValue = this._versionType == null ? null : this._versionType.Namespace; | |
| if (System.String.IsNullOrEmpty(namespaceValue)) | |
| return string.Empty; | |
| var namespaceParts = namespaceValue.Split('.'); | |
| var serviceId = namespaceParts[namespaceParts.Length - 1]; | |
| return serviceId; |
| window.addEventListener('scroll', function() { | ||
| var toc = document.getElementById('pageTOC'); | ||
| if (window.scrollY > 200) { | ||
| toc.classList.add('collapsed'); | ||
| document.getElementById('tocList').style.display = 'none'; | ||
| document.getElementById('tocToggle').innerHTML = '▶'; | ||
| } | ||
| }); |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scroll event handler does not check if the 'pageTOC', 'tocList', or 'tocToggle' elements exist before accessing them. If these elements are not present on the page (e.g., on pages that don't use the TOC feature), this will throw null reference errors. Add null checks before accessing these elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this valid? are there pages where toc wont exist that this will be executed?
| GenerationManifest coreManifest = null; | ||
| DeferredTypesProvider deferredTypes = new DeferredTypesProvider(null); | ||
|
|
||
| // Generate the Code Examples fragments for all services |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ExampleMetaJson parameter is passed directly to GenerateExampleFragments without checking if it's null or empty. While GenerateExampleFragments does check if the file exists, if options.ExampleMetaJson is null, this will cause File.Exists to throw an ArgumentNullException. Consider adding validation here or ensuring GenerateExampleFragments handles null/empty paths gracefully.
| // Generate the Code Examples fragments for all services | |
| // Generate the Code Examples fragments for all services | |
| if (string.IsNullOrEmpty(options.ExampleMetaJson)) | |
| { | |
| Info("ERROR: ExampleMetaJson option not set"); | |
| return -1; | |
| } |
Description
This change updates the doc generator to generate client and interface page Code Examples links contained in the example_meta.json file. The checked in example_meta.json file is not the full file which is injected by the build system.
A new "In this article" control is added to the client and interface pages for jumping to the associated sections of the page including the new code examples section. It is page section context aware and will only include the jump links if the section of the page exists. We may apply the control to method and other pages in the future.
Related internal CR for build system modifications and additional context: CR-241670200
Motivation and Context
Backlog: DOTNET-8196 -- Adds code examples links to client and interface pages where available.
Testing
Screenshots (if appropriate)
Types of changes
Checklist
License