-
Notifications
You must be signed in to change notification settings - Fork 12
feat: add schema complexity analyzer command #165
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
Open
vishalg0wda
wants to merge
2
commits into
main
Choose a base branch
from
feat/schema-complexity-analyzer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| package openapi | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
|
|
||
| tea "github.com/charmbracelet/bubbletea" | ||
| "github.com/speakeasy-api/openapi/cmd/openapi/internal/analyze" | ||
| "github.com/speakeasy-api/openapi/cmd/openapi/internal/analyze/tui" | ||
| "github.com/spf13/cobra" | ||
| "golang.org/x/term" | ||
| ) | ||
|
|
||
| var analyzeCmd = &cobra.Command{ | ||
| Use: "analyze <file>", | ||
| Short: "Analyze schema complexity, cyclicality, and codegen difficulty", | ||
| Long: `Analyze an OpenAPI specification to understand schema complexity. | ||
|
|
||
| This command examines schema references to identify: | ||
| - Cycles and strongly connected components (SCCs) | ||
| - Per-schema complexity metrics (fan-in, fan-out, nesting) | ||
| - Code generation difficulty tiers (green/yellow/red) | ||
| - Actionable refactoring suggestions | ||
|
|
||
| Output formats: | ||
| tui - Interactive terminal UI with progressive disclosure (default) | ||
| json - Machine-readable JSON report for CI/CD pipelines | ||
| text - Human-readable text summary | ||
| dot - Graphviz DOT format for graph visualization | ||
| mermaid - Mermaid diagram syntax | ||
|
|
||
| The TUI format auto-falls back to text when stdout is not a terminal. | ||
|
|
||
| Stdin is supported — pipe data or use '-': | ||
| cat spec.yaml | openapi spec analyze | ||
| cat spec.yaml | openapi spec analyze - --format json`, | ||
| Args: stdinOrFileArgs(1, 1), | ||
| RunE: runAnalyze, | ||
| } | ||
|
|
||
| func init() { | ||
| analyzeCmd.Flags().StringP("format", "f", "tui", "output format: tui, json, text, dot, mermaid") | ||
| analyzeCmd.Flags().StringP("output", "o", "", "write output to file instead of stdout") | ||
| } | ||
|
|
||
| func runAnalyze(cmd *cobra.Command, args []string) error { | ||
| ctx := cmd.Context() | ||
| inputFile := inputFileFromArgs(args) | ||
| format, _ := cmd.Flags().GetString("format") | ||
| outputFile, _ := cmd.Flags().GetString("output") | ||
|
|
||
| // Load the document | ||
| doc, err := loadOpenAPIDocument(ctx, inputFile) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Run analysis | ||
| report := analyze.Analyze(ctx, doc) | ||
|
|
||
| // Auto-fallback: if format is TUI but stdout is not a terminal, use text | ||
| if format == "tui" && outputFile == "" && !term.IsTerminal(int(os.Stdout.Fd())) { | ||
| format = "text" | ||
| } | ||
|
|
||
| // TUI is incompatible with --output; suggest text instead | ||
| if format == "tui" && outputFile != "" { | ||
| return fmt.Errorf("--output is not compatible with --format tui; use --format text, json, or dot instead") | ||
| } | ||
|
|
||
| // Open output writer | ||
| var w io.Writer = os.Stdout | ||
| if outputFile != "" { | ||
| f, err := os.Create(outputFile) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to create output file: %w", err) | ||
| } | ||
| defer f.Close() | ||
| w = f | ||
| } | ||
|
|
||
| switch format { | ||
| case "tui": | ||
| m := tui.NewModel(report) | ||
| p := tea.NewProgram(m, tea.WithAltScreen()) | ||
| if _, err := p.Run(); err != nil { | ||
| return fmt.Errorf("error running analyzer TUI: %w", err) | ||
| } | ||
| return nil | ||
|
|
||
| case "json": | ||
| return analyze.WriteJSON(w, report) | ||
|
|
||
| case "text": | ||
| analyze.WriteText(w, report) | ||
| return nil | ||
|
|
||
| case "dot": | ||
| analyze.WriteDOT(w, report) | ||
| return nil | ||
|
|
||
| case "mermaid": | ||
| analyze.WriteMermaid(w, report) | ||
| return nil | ||
|
|
||
| default: | ||
| return fmt.Errorf("unknown format: %s (expected tui, json, text, dot, or mermaid)", format) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 auto-fallback for
--format tuionly checks whether stdout is a terminal. When the spec is read from stdin (e.g.cat spec.yaml | openapi spec analyze), stdin is not a TTY, so Bubble Tea won't receive keyboard input (stdin will be consumed/EOF) even though stdout is a TTY. To match the documented behavior, also detect non-TTY stdin /IsStdin(inputFile)and fall back to text (or explicitly open/dev/ttyand pass it viatea.WithInput).