├── CONTRIBUTING.md ├── .gitignore ├── logo.png ├── insomnia.png ├── swagger.png ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── examples ├── config.json └── watchnow.json ├── Dockerfile ├── models ├── insomnia.go └── resource.go ├── Dockerfile.multi ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── stale.yml └── workflows │ └── go.yml ├── LICENSE ├── tmpl └── swagger.yaml ├── app.go ├── README.md ├── CODE_OF_CONDUCT.md ├── swagger.go ├── logo.svg └── template.go /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How Can I Contribute? 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.json 2 | swagger.yaml 3 | swaggomnia 4 | test.json 5 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fyb3roptik/swaggomnia/HEAD/logo.png -------------------------------------------------------------------------------- /insomnia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fyb3roptik/swaggomnia/HEAD/insomnia.png -------------------------------------------------------------------------------- /swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fyb3roptik/swaggomnia/HEAD/swagger.png -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected behavior 2 | 3 | ### Current behavior 4 | 5 | ### Steps to reproduce the behavior 6 | 7 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | ### Related Issue 4 | 5 | ### Changes proposed 6 | 7 | ### Screenshots 8 | -------------------------------------------------------------------------------- /examples/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "title" : "WatchNow API", 3 | "version" : "1.0.0", 4 | "basePath" : "https://api.domain.com/v1", 5 | "description" : "This API allows you to get in real time movies playing in theatre." 6 | } 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.11 2 | MAINTAINER Fyb3roptik 3 | WORKDIR /go/src/github.com/fyb3roptik/swaggomnia/ 4 | COPY . . 5 | RUN go get -v 6 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o swaggomnia . 7 | ENTRYPOINT ["./swaggomnia"] 8 | -------------------------------------------------------------------------------- /models/insomnia.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type Insomnia struct { 6 | Type string `json:"_type"` 7 | ExportFormat int `json:"__export_format"` 8 | ExportDate time.Time `json:"__export_date"` 9 | ExportSource string `json:"__export_source"` 10 | Resources []Resource `json:"resources"` 11 | } 12 | -------------------------------------------------------------------------------- /Dockerfile.multi: -------------------------------------------------------------------------------- 1 | FROM golang:1.8 AS builder 2 | MAINTAINER Fyb3roptik 3 | WORKDIR /go/src/github.com/Fyb3roptik/swaggomnia/ 4 | COPY . . 5 | RUN go get -v 6 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o swaggymnia . 7 | 8 | FROM alpine:latest 9 | RUN apk --no-cache add ca-certificates 10 | WORKDIR /root/ 11 | COPY --from=builder /go/src/github.com/Fyb3roptik/swaggomnia/swaggomnia . 12 | ENTRYPOINT ["./swaggomnia"] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Swaggomnia Build 2 | on: 3 | push: 4 | tags: 5 | - 2.* 6 | jobs: 7 | 8 | build: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | steps: 12 | 13 | - name: Set up Go 1.13 14 | uses: actions/setup-go@v1 15 | with: 16 | go-version: 1.13 17 | id: go 18 | 19 | - name: Check out code into the Go module directory 20 | uses: actions/checkout@v1 21 | 22 | - name: Get dependencies 23 | run: | 24 | go get -v -t -d ./... 25 | if [ -f Gopkg.toml ]; then 26 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 27 | dep ensure 28 | fi 29 | 30 | - name: Build 31 | uses: izumin5210/action-go-crossbuild@v1.0.0 32 | 33 | - name: Release 34 | uses: softprops/action-gh-release@v0.1.5 35 | if: startsWith(github.ref, 'refs/tags/') 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | with: 39 | files: dist/* 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Fyb3roptik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tmpl/swagger.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | description: "{{ .Config.Description }}" 4 | version: "{{ .Config.Version }}" 5 | title: "{{ .Config.Title }}" 6 | servers: 7 | - url: "{{ .Config.BasePath }}" 8 | tags: 9 | {{ range $key, $value := .Entities }} 10 | - name: "{{ $key | GetGroupName }}" 11 | description: "" 12 | {{ end }} 13 | paths: 14 | {{- range $key, $paths := .Entities }} 15 | {{- range $path, $resources := $paths }} 16 | {{ $path | RemovePathPrefix}}: 17 | {{- range $kk, $resource := $resources }} 18 | {{ $resource.Method | ToLower }}: 19 | tags: 20 | - "{{ $key | GetGroupName }}" 21 | summary: "{{ $resource.Name }}" 22 | {{- if or $resource.Parameters $resource.Body.Params $resource.Headers }} 23 | {{- if $resource.Body.MimeType }} 24 | description: "{{ $resource.Description }}" 25 | {{- end }} 26 | parameters: 27 | {{- range $p, $param := $resource.Parameters}} 28 | - name: "{{ $param.Name }}" 29 | in: "query" 30 | schema: 31 | type: "string" 32 | description: "" 33 | required: true 34 | {{- end }} 35 | {{- range $p, $param := $resource.Headers}} 36 | - name: "{{ $param.Name }}" 37 | in: "header" 38 | schema: 39 | type: "string" 40 | description: "" 41 | required: true 42 | {{- end }} 43 | {{- end }} 44 | responses: 45 | 200: 46 | description: "successful operation" 47 | {{- end }} 48 | {{- end }} 49 | {{- end }} 50 | -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | func main() { 12 | app := cli.NewApp() 13 | app.Name = "swaggonmia" 14 | app.Usage = "Insomnia to Swagger converter" 15 | app.Version = "1.1.0" 16 | app.Compiled = time.Now() 17 | app.Authors = []*cli.Author{ 18 | &cli.Author{ 19 | Name: "Nick Wallace", 20 | Email: "nwallace@fyberstudios.com", 21 | }, 22 | } 23 | app.Commands = []*cli.Command{ 24 | { 25 | Name: "generate", 26 | Aliases: []string{"g"}, 27 | Flags: []cli.Flag{ 28 | &cli.StringFlag{ 29 | Name: "config, c", 30 | Usage: "Load configuration from `FILE`", 31 | }, 32 | &cli.StringFlag{ 33 | Name: "insomnia, i", 34 | Usage: "Insomnia JSON `FILE`", 35 | }, 36 | &cli.StringFlag{ 37 | Name: "output, o", 38 | Value: "yaml", 39 | Usage: "Output json|yaml", 40 | }, 41 | }, 42 | Usage: "Generate Swagger documentation", 43 | Action: func(c *cli.Context) error { 44 | var insomniaFile = c.String("insomnia") 45 | var configFile = c.String("config") 46 | var outputFormat = c.String("output") 47 | 48 | if insomniaFile == "" || configFile == "" { 49 | cli.ShowCompletions(c) 50 | } 51 | 52 | if outputFormat == "" { 53 | outputFormat = "json" 54 | } 55 | 56 | swagger := &Swagger{} 57 | swagger.Generate(insomniaFile, configFile, outputFormat) 58 | 59 | return nil 60 | }, 61 | }, 62 | } 63 | app.CommandNotFound = func(c *cli.Context, command string) { 64 | fmt.Fprintf(c.App.Writer, "Wrong command %q !", command) 65 | } 66 | app.Run(os.Args) 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 | ![Swaggomnia Build](https://github.com/Fyb3roptik/swaggomnia/workflows/Swaggomnia%20Build/badge.svg) 9 | 10 | ## Description 11 | 12 | THIS IS A DEAD REPO NOW. Insomnia should now support this internally. 13 | 14 | Generate Swagger Documentation from Insomnia REST Client. 15 |
16 | *This is a fork of [swaggymnia](https://github.com/mlabouardy/swaggymnia) which is now dead.* 17 | 18 | ## Support 19 | [![Swaggomnia chat](https://badges.gitter.im/swaggomnia/gitter.png)](https://gitter.im/swaggomnia/community) 20 | 21 | ## Changelog 22 | 23 | `2.0.1` 24 | * Updated Logo 25 | 26 | `2.0` 27 | * Open API 3 support 28 | * Insomnia v4 support 29 | * New Config Format 30 | 31 | ## How to use it 32 | 33 | See usage with: 34 | 35 | ``` 36 | $ swaggomnia --help 37 | ``` 38 | 39 | Generate Swagger documentation: 40 | 41 | ``` 42 | $ swaggomnia generate -insomnia INSOMNIA_EXPORTED_FILE -config CONFIG_FILE -output FORMAT 43 | ``` 44 | 45 | | Option | Description | 46 | | ------ | ----------- | 47 | | -insomnia | Insomnia exported file | 48 | | -config | API Global Configuration file (see [Configuration Format](#configuration-format))| 49 | | -output | Insomnia output format (json or yaml, default json) | 50 | 51 | 52 | ## Example 53 | 54 | Let's convert the following Insomnia API documentation to Swagger: 55 | 56 |
57 | 58 |
59 | 60 | Issue the following command: 61 | 62 | ``` 63 | $ swaggomnia generate -i examples/watchnow.json -c examples/config.json -o json 64 | ``` 65 | 66 |
67 | 68 |
69 | 70 | ## Configuration Format 71 | 72 | ``` 73 | { 74 | "title" : "API Name", 75 | "version" : "API version", 76 | "basePath" : "https://api.domain.com/v1", 77 | "description" : "API description" 78 | } 79 | ``` 80 | 81 | ## Maintainers 82 | - Nick Wallace - nwallace@fyberstudios.com - New Author 83 | 84 | ## License 85 | 86 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details 87 | -------------------------------------------------------------------------------- /models/resource.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Resource struct { 4 | ID string `json:"_id"` 5 | ParentID string `json:"parentId"` 6 | Modified int64 `json:"modified"` 7 | Created int64 `json:"created"` 8 | Name string `json:"name"` 9 | Description string `json:"description,omitempty"` 10 | Certificates []interface{} `json:"certificates,omitempty"` 11 | Type string `json:"_type"` 12 | Data DataUrl `json:"data,omitempty"` 13 | Color interface{} `json:"color,omitempty"` 14 | Cookies []interface{} `json:"cookies,omitempty"` 15 | Environment struct{} `json:"environment,omitempty"` 16 | URL string `json:"url,omitempty"` 17 | Method string `json:"method,omitempty"` 18 | Body EntityBody `json:"body,omitempty"` 19 | Parameters []QueryParameters `json:"parameters,omitempty"` 20 | Headers []EntityHeader `json:"headers,omitempty"` 21 | Authentication struct{} `json:"authentication,omitempty"` 22 | SettingStoreCookies bool `json:"settingStoreCookies,omitempty"` 23 | SettingSendCookies bool `json:"settingSendCookies,omitempty"` 24 | SettingDisableRenderRequestBody bool `json:"settingDisableRenderRequestBody,omitempty"` 25 | SettingEncodeURL bool `json:"settingEncodeUrl,omitempty"` 26 | InsomniaParams []string `json:"insomnia_params,omitempty"` 27 | } 28 | 29 | type EntityBody struct { 30 | MimeType string `json:"mimeType"` 31 | Params []EntityParam `json:"params"` 32 | } 33 | 34 | type EntityParam struct { 35 | Name string `json:"name"` 36 | Value string `json:"value"` 37 | ID string `json:"id"` 38 | Disabled bool `json:"disabled"` 39 | } 40 | type QueryParameters struct { 41 | Name string `json:"name"` 42 | Value string `json:"value"` 43 | } 44 | type EntityHeader struct { 45 | Name string `json:"name"` 46 | Value string `json:"value"` 47 | } 48 | 49 | type DataUrl struct { 50 | BaseURL string `json:"base_url"` 51 | RefURL string `json:"ref_url"` 52 | } 53 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at nwallace@fyberstudios.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /swagger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "html/template" 7 | "io/ioutil" 8 | "log" 9 | "os" 10 | "regexp" 11 | "strings" 12 | 13 | "github.com/ghodss/yaml" 14 | 15 | . "github.com/Fyb3roptik/swaggomnia/models" 16 | ) 17 | 18 | var groupNames map[string]string 19 | 20 | const ( 21 | REQUEST_GROUP = "request_group" 22 | REQUEST = "request" 23 | YAML_FORMAT = "yaml" 24 | JSON_FORMAT = "json" 25 | ) 26 | 27 | type Swagger struct { 28 | Config SwaggerConfig 29 | Entities map[string]map[string][]Resource 30 | } 31 | 32 | type SwaggerConfig struct { 33 | Title string `json:"title"` 34 | Version string `json:"version"` 35 | Host string `json:"host"` 36 | BasePath string `json:"basePath"` 37 | Schemes string `json:"schemes"` 38 | Description string `json:"description"` 39 | } 40 | 41 | func parse(insomnia Insomnia) map[string]map[string][]Resource { 42 | groupNames = make(map[string]string) 43 | entities := make(map[string]map[string][]Resource) 44 | for _, resource := range insomnia.Resources { 45 | if resource.Type == REQUEST_GROUP { 46 | groupNames[resource.ID] = resource.Name 47 | entities[resource.ID] = make(map[string][]Resource, 0) 48 | } 49 | if resource.Type == REQUEST { 50 | fetchVariables(&resource) 51 | if entities[resource.ParentID] == nil { 52 | entities[resource.ParentID] = make(map[string][]Resource, 0) 53 | } 54 | entities[resource.ParentID][resource.URL] = append(entities[resource.ParentID][resource.URL], resource) 55 | } 56 | } 57 | return entities 58 | } 59 | 60 | func readInsomniaExport(fileName string) Insomnia { 61 | raw, err := ioutil.ReadFile(fileName) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | var insomnia Insomnia 66 | if err := json.Unmarshal(raw, &insomnia); err != nil { 67 | log.Fatal(err) 68 | } 69 | return insomnia 70 | } 71 | 72 | func readSwaggerConfig(fileName string) SwaggerConfig { 73 | raw, err := ioutil.ReadFile(fileName) 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | var config SwaggerConfig 78 | if err := json.Unmarshal(raw, &config); err != nil { 79 | log.Fatal(err) 80 | } 81 | return config 82 | } 83 | 84 | func (s *Swagger) Generate(insomniaFile string, configFile string, outputFormat string) { 85 | s.Config = readSwaggerConfig(configFile) 86 | s.Entities = parse(readInsomniaExport(insomniaFile)) 87 | switch outputFormat { 88 | case YAML_FORMAT: 89 | s.generateYAML() 90 | break 91 | case JSON_FORMAT: 92 | s.generateJSON() 93 | break 94 | default: 95 | log.Fatal("Only json or yaml formats are supported") 96 | } 97 | } 98 | 99 | func (s Swagger) initTemplate() *template.Template { 100 | funcMap := template.FuncMap{ 101 | "ToLower": strings.ToLower, 102 | "RemovePathPrefix": func(path string) string { 103 | re := regexp.MustCompile("{{(.*?)}}") 104 | for _, param := range re.FindAllStringSubmatch(path, -1) { 105 | path = strings.Replace(path, param[0], "", -1) 106 | } 107 | if strings.ContainsAny(path, s.Config.BasePath) { 108 | path = strings.Replace(path, s.Config.BasePath, "", -1) 109 | } 110 | return path 111 | }, 112 | "GetGroupName": func(id string) string { 113 | return groupNames[id] 114 | }, 115 | } 116 | data, err := Asset("tmpl/swagger.yaml") 117 | if err != nil { 118 | log.Fatal(err) 119 | } 120 | tmpl, err := template.New("swagger.yaml").Funcs(funcMap).Parse(string(data)) 121 | if err != nil { 122 | log.Fatal(err) 123 | } 124 | return tmpl 125 | } 126 | 127 | func (s Swagger) generateJSON() { 128 | tmpl := s.initTemplate() 129 | 130 | var tpl bytes.Buffer 131 | err := tmpl.Execute(&tpl, s) 132 | if err != nil { 133 | log.Fatal(err) 134 | } 135 | data, err := yaml.YAMLToJSON(tpl.Bytes()) 136 | if err != nil { 137 | log.Fatal(err) 138 | } 139 | err = ioutil.WriteFile("swagger.json", data, 0644) 140 | if err != nil { 141 | log.Fatal(err) 142 | } 143 | } 144 | 145 | func (s Swagger) generateYAML() { 146 | tmpl := s.initTemplate() 147 | 148 | f, err := os.Create("swagger.yaml") 149 | if err != nil { 150 | log.Fatal(err) 151 | } 152 | 153 | err = tmpl.Execute(f, s) 154 | if err != nil { 155 | log.Fatal(err) 156 | } 157 | } 158 | 159 | func fetchVariables(resource *Resource) { 160 | re := regexp.MustCompile("/{{ (.*?) }}#*") 161 | for _, param := range re.FindAllStringSubmatch(resource.URL, -1) { 162 | resource.InsomniaParams = append(resource.InsomniaParams, param[1]) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | logo -------------------------------------------------------------------------------- /examples/watchnow.json: -------------------------------------------------------------------------------- 1 | { 2 | "_type": "export", 3 | "__export_format": 3, 4 | "__export_date": "2017-10-24T12:18:15.542Z", 5 | "__export_source": "insomnia.desktop.app:v5.9.6", 6 | "resources": [ 7 | { 8 | "_id": "wrk_aa76b042e4f040669890662ba8819fd2", 9 | "parentId": null, 10 | "modified": 1508847165351, 11 | "created": 1508847165351, 12 | "name": "WatchNow API", 13 | "description": "", 14 | "certificates": [], 15 | "_type": "workspace" 16 | }, 17 | { 18 | "_id": "env_5e2660615b724bd59560a19a1a284638", 19 | "parentId": "wrk_aa76b042e4f040669890662ba8819fd2", 20 | "modified": 1508847165367, 21 | "created": 1508847165367, 22 | "name": "Base Environment", 23 | "data": {}, 24 | "color": null, 25 | "isPrivate": false, 26 | "_type": "environment" 27 | }, 28 | { 29 | "_id": "jar_f77b2d8ddb0b4d2eb0d679fe8add0e8f", 30 | "parentId": "wrk_aa76b042e4f040669890662ba8819fd2", 31 | "modified": 1508847165356, 32 | "created": 1508847165356, 33 | "name": "Default Jar", 34 | "cookies": [], 35 | "_type": "cookie_jar" 36 | }, 37 | { 38 | "_id": "fld_0b5a5fd953be4748bef9b79963c1a2eb", 39 | "parentId": "wrk_aa76b042e4f040669890662ba8819fd2", 40 | "modified": 1508847172396, 41 | "created": 1508847172396, 42 | "name": "Movies", 43 | "description": "", 44 | "environment": {}, 45 | "metaSortKey": -1508847172396, 46 | "_type": "request_group" 47 | }, 48 | { 49 | "_id": "fld_2bffe2b85ebd43b9a11a6446378491be", 50 | "parentId": "wrk_aa76b042e4f040669890662ba8819fd2", 51 | "modified": 1508847192996, 52 | "created": 1508847192996, 53 | "name": "Series", 54 | "description": "", 55 | "environment": {}, 56 | "metaSortKey": -1508847192996, 57 | "_type": "request_group" 58 | }, 59 | { 60 | "_id": "fld_e1ca0cc87fb44514ba659141e09ce037", 61 | "parentId": "wrk_aa76b042e4f040669890662ba8819fd2", 62 | "modified": 1508847197901, 63 | "created": 1508847197901, 64 | "name": "Anime", 65 | "description": "", 66 | "environment": {}, 67 | "metaSortKey": -1508847197901, 68 | "_type": "request_group" 69 | }, 70 | { 71 | "_id": "env_769f989c0019482f982b386730c42de6", 72 | "parentId": "env_5e2660615b724bd59560a19a1a284638", 73 | "modified": 1508847406425, 74 | "created": 1508847378532, 75 | "name": "production", 76 | "data": { 77 | "api": "https://api.watchnow.com" 78 | }, 79 | "color": null, 80 | "isPrivate": false, 81 | "_type": "environment" 82 | }, 83 | { 84 | "_id": "req_38a7c430a0cc47c2946b8b11c955caac", 85 | "parentId": "fld_0b5a5fd953be4748bef9b79963c1a2eb", 86 | "modified": 1508847486881, 87 | "created": 1508847357268, 88 | "url": "{{api}}/movies", 89 | "name": "List of movies", 90 | "description": "", 91 | "method": "GET", 92 | "body": {}, 93 | "parameters": [], 94 | "headers": [], 95 | "authentication": {}, 96 | "metaSortKey": -1508847357268, 97 | "settingStoreCookies": true, 98 | "settingSendCookies": true, 99 | "settingDisableRenderRequestBody": false, 100 | "settingEncodeUrl": true, 101 | "_type": "request" 102 | }, 103 | { 104 | "_id": "req_d6409dcc43b84247a55cd50f4966dd15", 105 | "parentId": "fld_0b5a5fd953be4748bef9b79963c1a2eb", 106 | "modified": 1508847479726, 107 | "created": 1508847366916, 108 | "url": "{{api}}/movies/{id}", 109 | "name": "Update movie schedule", 110 | "description": "", 111 | "method": "PUT", 112 | "body": {}, 113 | "parameters": [], 114 | "headers": [], 115 | "authentication": {}, 116 | "metaSortKey": -1508847366916, 117 | "settingStoreCookies": true, 118 | "settingSendCookies": true, 119 | "settingDisableRenderRequestBody": false, 120 | "settingEncodeUrl": true, 121 | "_type": "request" 122 | }, 123 | { 124 | "_id": "req_226463dbf74f4f4bb8688a50f327ef7b", 125 | "parentId": "fld_2bffe2b85ebd43b9a11a6446378491be", 126 | "modified": 1508847473098, 127 | "created": 1508847322900, 128 | "url": "{{api}}/series", 129 | "name": "List of series", 130 | "description": "", 131 | "method": "GET", 132 | "body": {}, 133 | "parameters": [], 134 | "headers": [], 135 | "authentication": {}, 136 | "metaSortKey": -1508847322900, 137 | "settingStoreCookies": true, 138 | "settingSendCookies": true, 139 | "settingDisableRenderRequestBody": false, 140 | "settingEncodeUrl": true, 141 | "_type": "request" 142 | }, 143 | { 144 | "_id": "req_8ea31c7afa734d32bf370ac7f034a89f", 145 | "parentId": "fld_2bffe2b85ebd43b9a11a6446378491be", 146 | "modified": 1508847465767, 147 | "created": 1508847331100, 148 | "url": "{{api}}/series", 149 | "name": "Create a new serie", 150 | "description": "", 151 | "method": "POST", 152 | "body": {}, 153 | "parameters": [], 154 | "headers": [], 155 | "authentication": {}, 156 | "metaSortKey": -1508847331100, 157 | "settingStoreCookies": true, 158 | "settingSendCookies": true, 159 | "settingDisableRenderRequestBody": false, 160 | "settingEncodeUrl": true, 161 | "_type": "request" 162 | }, 163 | { 164 | "_id": "req_a1f1801dbf8542fd94de179f5e63ab84", 165 | "parentId": "fld_2bffe2b85ebd43b9a11a6446378491be", 166 | "modified": 1508847454230, 167 | "created": 1508847343485, 168 | "url": "{{api}}/series/{id}", 169 | "name": "Update a serie", 170 | "description": "", 171 | "method": "PUT", 172 | "body": {}, 173 | "parameters": [], 174 | "headers": [], 175 | "authentication": {}, 176 | "metaSortKey": -1508847343485, 177 | "settingStoreCookies": true, 178 | "settingSendCookies": true, 179 | "settingDisableRenderRequestBody": false, 180 | "settingEncodeUrl": true, 181 | "_type": "request" 182 | }, 183 | { 184 | "_id": "req_27c9c658c072400e894c571cab8bc18e", 185 | "parentId": "fld_2bffe2b85ebd43b9a11a6446378491be", 186 | "modified": 1508847445508, 187 | "created": 1508847351732, 188 | "url": "{{api}}/series/{id}", 189 | "name": "Delete a serie", 190 | "description": "", 191 | "method": "DELETE", 192 | "body": {}, 193 | "parameters": [], 194 | "headers": [], 195 | "authentication": {}, 196 | "metaSortKey": -1508847351732, 197 | "settingStoreCookies": true, 198 | "settingSendCookies": true, 199 | "settingDisableRenderRequestBody": false, 200 | "settingEncodeUrl": true, 201 | "_type": "request" 202 | }, 203 | { 204 | "_id": "req_fda82234158f4764902c1d453fdbe815", 205 | "parentId": "fld_e1ca0cc87fb44514ba659141e09ce037", 206 | "modified": 1508847435112, 207 | "created": 1508847260548, 208 | "url": "{{api}}/anime", 209 | "name": "List of anime", 210 | "description": "", 211 | "method": "GET", 212 | "body": {}, 213 | "parameters": [], 214 | "headers": [], 215 | "authentication": {}, 216 | "metaSortKey": -1508847260548, 217 | "settingStoreCookies": true, 218 | "settingSendCookies": true, 219 | "settingDisableRenderRequestBody": false, 220 | "settingEncodeUrl": true, 221 | "_type": "request" 222 | }, 223 | { 224 | "_id": "req_5743d10fd0864861822c5530c604f4fb", 225 | "parentId": "fld_e1ca0cc87fb44514ba659141e09ce037", 226 | "modified": 1508847418101, 227 | "created": 1508847315397, 228 | "url": "{{api}}/animes", 229 | "name": "Create anime", 230 | "description": "", 231 | "method": "POST", 232 | "body": {}, 233 | "parameters": [], 234 | "headers": [], 235 | "authentication": {}, 236 | "metaSortKey": -1508847315397, 237 | "settingStoreCookies": true, 238 | "settingSendCookies": true, 239 | "settingDisableRenderRequestBody": false, 240 | "settingEncodeUrl": true, 241 | "_type": "request" 242 | } 243 | ] 244 | } -------------------------------------------------------------------------------- /template.go: -------------------------------------------------------------------------------- 1 | // Package main Code generated by go-bindata. (@generated) DO NOT EDIT. 2 | // sources: 3 | // tmpl/swagger.yaml 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "compress/gzip" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "os" 13 | "path/filepath" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | func bindataRead(data []byte, name string) ([]byte, error) { 19 | gz, err := gzip.NewReader(bytes.NewBuffer(data)) 20 | if err != nil { 21 | return nil, fmt.Errorf("read %q: %v", name, err) 22 | } 23 | 24 | var buf bytes.Buffer 25 | _, err = io.Copy(&buf, gz) 26 | clErr := gz.Close() 27 | 28 | if err != nil { 29 | return nil, fmt.Errorf("read %q: %v", name, err) 30 | } 31 | if clErr != nil { 32 | return nil, err 33 | } 34 | 35 | return buf.Bytes(), nil 36 | } 37 | 38 | type asset struct { 39 | bytes []byte 40 | info os.FileInfo 41 | } 42 | 43 | type bindataFileInfo struct { 44 | name string 45 | size int64 46 | mode os.FileMode 47 | modTime time.Time 48 | } 49 | 50 | // Name return file name 51 | func (fi bindataFileInfo) Name() string { 52 | return fi.name 53 | } 54 | 55 | // Size return file size 56 | func (fi bindataFileInfo) Size() int64 { 57 | return fi.size 58 | } 59 | 60 | // Mode return file mode 61 | func (fi bindataFileInfo) Mode() os.FileMode { 62 | return fi.mode 63 | } 64 | 65 | // ModTime return file modify time 66 | func (fi bindataFileInfo) ModTime() time.Time { 67 | return fi.modTime 68 | } 69 | 70 | // IsDir return file whether a directory 71 | func (fi bindataFileInfo) IsDir() bool { 72 | return fi.mode&os.ModeDir != 0 73 | } 74 | 75 | // Sys return file is sys mode 76 | func (fi bindataFileInfo) Sys() interface{} { 77 | return nil 78 | } 79 | 80 | var _tmplSwaggerYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x52\x4d\x8f\xd3\x30\x10\xbd\xe7\x57\x3c\x55\x3d\x6e\xa3\x0a\x6e\x91\xb8\x2c\xa0\xe5\xc0\xa2\x0a\x55\xdc\xad\x64\xd2\x58\xdb\xd8\xde\xb1\x5d\x88\xda\xfc\x77\x64\xe7\x3b\x05\xad\x38\x91\x53\xfc\xfc\xe6\xbd\x79\xe3\xd1\x86\x94\x30\x32\xc3\xfb\x74\x9f\xee\x13\xa9\x4a\x9d\x25\x40\x41\x36\x67\x69\x9c\xd4\x2a\xc3\xe6\x7a\x45\xfa\x51\xab\x52\x9e\xd2\x4f\xd3\x05\xda\x76\x93\x00\x17\x62\x7b\x47\xfb\xd1\x81\x3d\xc5\x49\x77\xa6\x25\xe1\x18\xa0\x78\x6d\x89\x83\x44\x70\xdd\xc1\xf3\x79\xc9\x7b\x14\x96\x0e\xc2\x55\x91\xea\xc4\xc9\x66\xc9\xf5\x0a\x16\xea\x44\xd8\xbe\x50\xf3\x80\xed\x45\x9c\x3d\x21\xfb\x80\xf4\xb3\x72\xd2\x49\xb2\x68\xdb\x64\x07\x25\xea\xde\x34\x10\x71\xc3\x13\xb9\x27\xd6\xde\x7c\x13\x35\xf5\x9d\x2d\x73\x6e\x82\x36\xa9\x22\xd4\x1b\xe1\xaa\x68\xb6\x5b\xba\x45\xfc\xce\x6d\x46\x0b\x84\x07\x6c\x99\xac\xf6\x9c\x53\xe4\xf6\x55\x6d\x9b\x00\xa1\x9f\x70\xc4\x0d\xdf\xa9\xd6\x97\x18\xef\xc0\x54\xca\x5f\x6d\x1b\xa6\x10\x28\x93\xe9\xcb\x4c\x2b\x4a\x4d\xc2\x51\xae\x13\x1c\xc0\xf4\x99\x5c\xa5\x0b\xdc\x70\xd4\x5f\xf5\x4f\x62\x0c\x9a\x40\x37\xbd\xee\x7f\xf7\xc6\x5c\xc2\x67\x7d\x5d\x0b\x6e\xfa\x11\x8e\x0e\x2b\x56\xe8\x55\x96\xd0\x3c\xa3\x1c\x04\x8b\x9a\x1c\xb1\x9d\x81\x8f\xba\x68\xba\x9b\x39\xfa\x85\x44\x11\x78\x7d\x96\x51\x6f\x55\xf7\x2c\x6b\x3a\x36\x86\x26\xde\xdd\x86\x4e\x15\xf7\x3b\x3a\x08\xf7\x6f\xdb\x01\x66\xec\x32\x9b\x51\x86\x57\x8c\x4f\xcd\xa2\x5e\xcc\x7c\x96\x6c\x94\x59\x2c\x5a\x2c\x59\x8f\x08\x90\xa1\xc7\x57\x4f\xdc\x4c\x98\xcd\x2b\xaa\x45\x36\x9e\x01\xd7\x98\xa0\x63\x1d\x4b\x75\x9a\x88\xeb\x1d\x1d\x70\xa6\x57\x2f\x99\x8a\x0c\x8e\x3d\xfd\x2d\xe5\xdb\x99\xfa\x27\xf8\xf7\x40\x55\x2c\xfc\x2f\x89\x16\x00\x93\x35\x5a\x59\xb2\x93\xf5\xbb\xfd\x7e\xde\xc7\xd2\xd0\xfa\x3c\x27\x6b\x4b\x7f\x86\x36\xc4\x22\xc0\x9b\x64\x25\xfd\xe7\xdf\xdf\x01\x00\x00\xff\xff\x0d\xe0\x23\xe5\x2f\x05\x00\x00") 81 | 82 | func tmplSwaggerYamlBytes() ([]byte, error) { 83 | return bindataRead( 84 | _tmplSwaggerYaml, 85 | "tmpl/swagger.yaml", 86 | ) 87 | } 88 | 89 | func tmplSwaggerYaml() (*asset, error) { 90 | bytes, err := tmplSwaggerYamlBytes() 91 | if err != nil { 92 | return nil, err 93 | } 94 | 95 | info := bindataFileInfo{name: "tmpl/swagger.yaml", size: 1327, mode: os.FileMode(420), modTime: time.Unix(1582394795, 0)} 96 | a := &asset{bytes: bytes, info: info} 97 | return a, nil 98 | } 99 | 100 | // Asset loads and returns the asset for the given name. 101 | // It returns an error if the asset could not be found or 102 | // could not be loaded. 103 | func Asset(name string) ([]byte, error) { 104 | cannonicalName := strings.Replace(name, "\\", "/", -1) 105 | if f, ok := _bindata[cannonicalName]; ok { 106 | a, err := f() 107 | if err != nil { 108 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) 109 | } 110 | return a.bytes, nil 111 | } 112 | return nil, fmt.Errorf("Asset %s not found", name) 113 | } 114 | 115 | // MustAsset is like Asset but panics when Asset would return an error. 116 | // It simplifies safe initialization of global variables. 117 | func MustAsset(name string) []byte { 118 | a, err := Asset(name) 119 | if err != nil { 120 | panic("asset: Asset(" + name + "): " + err.Error()) 121 | } 122 | 123 | return a 124 | } 125 | 126 | // AssetInfo loads and returns the asset info for the given name. 127 | // It returns an error if the asset could not be found or 128 | // could not be loaded. 129 | func AssetInfo(name string) (os.FileInfo, error) { 130 | cannonicalName := strings.Replace(name, "\\", "/", -1) 131 | if f, ok := _bindata[cannonicalName]; ok { 132 | a, err := f() 133 | if err != nil { 134 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) 135 | } 136 | return a.info, nil 137 | } 138 | return nil, fmt.Errorf("AssetInfo %s not found", name) 139 | } 140 | 141 | // AssetNames returns the names of the assets. 142 | func AssetNames() []string { 143 | names := make([]string, 0, len(_bindata)) 144 | for name := range _bindata { 145 | names = append(names, name) 146 | } 147 | return names 148 | } 149 | 150 | // _bindata is a table, holding each asset generator, mapped to its name. 151 | var _bindata = map[string]func() (*asset, error){ 152 | "tmpl/swagger.yaml": tmplSwaggerYaml, 153 | } 154 | 155 | // AssetDir returns the file names below a certain 156 | // directory embedded in the file by go-bindata. 157 | // For example if you run go-bindata on data/... and data contains the 158 | // following hierarchy: 159 | // data/ 160 | // foo.txt 161 | // img/ 162 | // a.png 163 | // b.png 164 | // then AssetDir("data") would return []string{"foo.txt", "img"} 165 | // AssetDir("data/img") would return []string{"a.png", "b.png"} 166 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error 167 | // AssetDir("") will return []string{"data"}. 168 | func AssetDir(name string) ([]string, error) { 169 | node := _bintree 170 | if len(name) != 0 { 171 | cannonicalName := strings.Replace(name, "\\", "/", -1) 172 | pathList := strings.Split(cannonicalName, "/") 173 | for _, p := range pathList { 174 | node = node.Children[p] 175 | if node == nil { 176 | return nil, fmt.Errorf("Asset %s not found", name) 177 | } 178 | } 179 | } 180 | if node.Func != nil { 181 | return nil, fmt.Errorf("Asset %s not found", name) 182 | } 183 | rv := make([]string, 0, len(node.Children)) 184 | for childName := range node.Children { 185 | rv = append(rv, childName) 186 | } 187 | return rv, nil 188 | } 189 | 190 | type bintree struct { 191 | Func func() (*asset, error) 192 | Children map[string]*bintree 193 | } 194 | 195 | var _bintree = &bintree{nil, map[string]*bintree{ 196 | "tmpl": &bintree{nil, map[string]*bintree{ 197 | "swagger.yaml": &bintree{tmplSwaggerYaml, map[string]*bintree{}}, 198 | }}, 199 | }} 200 | 201 | // RestoreAsset restores an asset under the given directory 202 | func RestoreAsset(dir, name string) error { 203 | data, err := Asset(name) 204 | if err != nil { 205 | return err 206 | } 207 | info, err := AssetInfo(name) 208 | if err != nil { 209 | return err 210 | } 211 | err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) 212 | if err != nil { 213 | return err 214 | } 215 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) 216 | if err != nil { 217 | return err 218 | } 219 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) 220 | if err != nil { 221 | return err 222 | } 223 | return nil 224 | } 225 | 226 | // RestoreAssets restores an asset under the given directory recursively 227 | func RestoreAssets(dir, name string) error { 228 | children, err := AssetDir(name) 229 | // File 230 | if err != nil { 231 | return RestoreAsset(dir, name) 232 | } 233 | // Dir 234 | for _, child := range children { 235 | err = RestoreAssets(dir, filepath.Join(name, child)) 236 | if err != nil { 237 | return err 238 | } 239 | } 240 | return nil 241 | } 242 | 243 | func _filePath(dir, name string) string { 244 | cannonicalName := strings.Replace(name, "\\", "/", -1) 245 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) 246 | } 247 | --------------------------------------------------------------------------------