Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
idsulik committed Sep 15, 2024
1 parent e42a02e commit 82e53aa
Show file tree
Hide file tree
Showing 14 changed files with 630 additions and 3 deletions.
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
.idea

# Binaries for programs and plugins
*.exe
*.exe~
Expand Down
104 changes: 104 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Swaga CLI Tool

Swaga is a simple CLI tool to list, view, and convert Swagger API endpoints. It helps you explore the API structure from a Swagger/OpenAPI specification file and convert API requests into common formats like `curl`, `fetch`.

## Features

- **List**: Display all API endpoints from a Swagger file in a structured table format.
- **View**: View detailed information about a specific API endpoint.
- **Convert**: Convert an API endpoint into `curl`, `fetch` request formats.

## Installation

You can install `swaga` by cloning the repository and building the binary:

```bash
git clone https://github.com/idsulik/swama
cd swama
go build -o swaga
```

Ensure that the binary is placed in a directory included in your `PATH`.

## Usage

Swaga provides several commands for interacting with your Swagger/OpenAPI file:

### General Usage

```bash
swaga [command]
```

### Available Commands

- **`completion`**: Generate the autocompletion script for your shell.
- **`convert`**: Convert an API endpoint to different request formats (e.g., `curl`, `fetch`).
- **`list`**: Lists all API endpoints in a structured table format.
- **`view`**: View details about a specific API endpoint.
- **`help`**: Show help for commands.

### Global Flags

- **`-f, --file`**: Path to the Swagger JSON/YAML file. If omitted, `swaga` will attempt to locate the Swagger file in the current directory.
- **`-h, --help`**: Display help for the `swaga` CLI.

### Examples

#### List all endpoints

```bash
swaga list -f ./swagger.json
```

This command lists all API endpoints defined in the `swagger.json` file.

#### View details of a specific endpoint

```bash
swaga view -f ./swagger.json --endpoint /api/users
```

This command will display detailed information about the `/api/users` endpoint.

#### Convert an endpoint to a `curl` command

```bash
swaga convert -f ./swagger.json --endpoint /api/users --type curl
```

This command converts the `/api/users` endpoint to a `curl` command.

#### Generate shell autocompletion

You can generate autocompletion scripts for your shell (e.g., bash, zsh, fish):

```bash
swaga completion bash > /etc/bash_completion.d/swaga
```

## More Information

For more details on each command, use the `--help` flag with the command:

```bash
swaga [command] --help
```

For example:

```bash
swaga list --help
```

## Contributing

Feel free to contribute to this project by submitting issues or pull requests at the official [GitHub repository](https://github.com/idsulik/swama).

## License

This project is licensed under the MIT License. See the `LICENSE` file for more details.

---

Swaga simplifies exploring and interacting with your Swagger-defined API, making it easier to understand and test API endpoints quickly.
9 changes: 9 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cmd

// GlobalConfig holds the global flags like swaggerPath.
type GlobalConfig struct {
SwaggerPath string
}

// Initialize the global config instance
var config GlobalConfig
48 changes: 48 additions & 0 deletions cmd/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmd

import (
"fmt"
"strings"

"github.com/spf13/cobra"
"swama/pkg/converter"
"swama/pkg/openapi"
)

var convertCmd = &cobra.Command{
Use: "convert",
Short: "Convert an endpoint to curl or fetch",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
parts := strings.Split(args[0], " ")
var method, endpoint string
if len(parts) == 1 {
method = "GET"
endpoint = parts[0]
} else {
method = parts[0]
endpoint = strings.Join(parts[1:], " ")
}
convertType := args[1]

swagger, err := openapi.ParseSwaggerFile(ctx, config.SwaggerPath)
if err != nil {
return fmt.Errorf("error parsing Swagger file: %w", err)
}

converter, err := converter.NewConverter(convertType)
if err != nil {
return fmt.Errorf("error creating converter: %w", err)
}

value, err := converter.ConvertEndpoint(swagger, method, endpoint)

if err != nil {
return fmt.Errorf("error converting endpoint: %w", err)
}

fmt.Println(value)
return nil
},
}
134 changes: 134 additions & 0 deletions cmd/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package cmd

import (
"fmt"
"maps"
"os"
"regexp"
"slices"

"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"swama/pkg/openapi"
)

type ListConfig struct {
method string
endpoint string
tag string
group string
}

const (
GroupByTag = "tag"
GroupByMethod = "method"
GroupByNone = "none"
)

// Command-specific flags for the list command
var listConfig = ListConfig{}

// listCmd represents the list command
var listCmd = &cobra.Command{
Use: "list",
Short: "Lists all API endpoints from a Swagger file",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
swagger, err := openapi.ParseSwaggerFile(ctx, config.SwaggerPath)
if err != nil {
return fmt.Errorf("error parsing Swagger file: %w", err)
}

type groupItem struct {
method string
path string
summary string
tags string
}
groupedEndpoints := make(map[string][]groupItem)
for _, path := range swagger.Paths.InMatchingOrder() {
for method, operation := range swagger.Paths.Find(path).Operations() {
if listConfig.endpoint != "" {
if matched, _ := regexp.MatchString(fmt.Sprintf("^%s$", listConfig.endpoint), path); !matched {
continue
}
}

if listConfig.method != "" && method != listConfig.method {
continue
}

if listConfig.tag != "" && !slices.Contains(operation.Tags, listConfig.tag) {
continue
}

tags := ""
if len(operation.Tags) > 0 {
tags = fmt.Sprintf("%v", operation.Tags)
}

if listConfig.group != "" {
keys := make([]string, 0)
if listConfig.group == GroupByTag {
for _, tag := range operation.Tags {
description := swagger.Tags.Get(tag).Description
keys = append(keys, fmt.Sprintf("%s (%s)", tag, description))
}
} else if listConfig.group == GroupByMethod {
keys = append(keys, method)
} else {
keys = append(keys, "none")
}

for _, key := range keys {
if _, ok := groupedEndpoints[key]; !ok {
groupedEndpoints[key] = make([]groupItem, 0)
}
groupedEndpoints[key] = append(
groupedEndpoints[key],
groupItem{
method: method,
path: path,
summary: operation.Summary,
tags: tags,
},
)
}
continue
}
}
}

// Sort and print the grouped endpoints
sortedKeys := slices.Sorted(maps.Keys(groupedEndpoints))
var table *tablewriter.Table
fmt.Println()
for _, key := range sortedKeys {
if key != GroupByNone {
fmt.Printf("%s\n", key)
}

table = tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Method", "Path", "Summary", "Tags"})
values := groupedEndpoints[key]
for _, value := range values {
table.Append([]string{value.method, value.path, value.summary, value.tags})
}

table.Render()

if key != GroupByNone {
fmt.Println()
}
}

return nil
},
}

func init() {
listCmd.Flags().StringVarP(&listConfig.endpoint, "endpoint", "e", "", "Filter by endpoint, supports wildcard")
listCmd.Flags().StringVarP(&listConfig.method, "method", "m", "", "Filter by method")
listCmd.Flags().StringVarP(&listConfig.tag, "tag", "t", "", "Filter by tag")
listCmd.Flags().StringVarP(&listConfig.group, "group", "g", GroupByTag, "Group output by tag, method")
}
38 changes: 38 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cmd

import (
"context"

"github.com/spf13/cobra"
"swama/pkg/openapi"
)

// rootCmd represents the base command
var rootCmd = &cobra.Command{
Use: "swaga",
Short: "CLI tool for Swagger/OpenAPI operations",
Long: `A simple CLI tool to list, view and convert Swagger endpoints.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if config.SwaggerPath == "" {
config.SwaggerPath = openapi.LocateSwaggerFile()
}
},
}

func Execute(ctx context.Context) error {
return rootCmd.ExecuteContext(ctx)
}

func init() {
rootCmd.PersistentFlags().StringVarP(
&config.SwaggerPath,
"file",
"f",
"",
"Path to the Swagger JSON/YAML file. If not provided, the tool will try to locate it.",
)
rootCmd.AddCommand(listCmd)
rootCmd.AddCommand(viewCmd)
rootCmd.AddCommand(convertCmd)
rootCmd.SetHelpCommand(nil)
}
32 changes: 32 additions & 0 deletions cmd/view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
"swama/pkg/openapi"
)

var viewCmd = &cobra.Command{
Use: "view",
Short: "View details of a specific endpoint",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
endpoint := args[0]

ctx := cmd.Context()
swagger, err := openapi.ParseSwaggerFile(ctx, config.SwaggerPath)
if err != nil {
return fmt.Errorf("error parsing Swagger file: %w", err)
}

details, err := openapi.ViewEndpointDetails(swagger, endpoint)

if err != nil {
return err
}

fmt.Println(details)
return nil
},
}
Loading

0 comments on commit 82e53aa

Please sign in to comment.