Skip to content
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

fix x-logo vendor extension & parse x-codeSamples from files #760

Merged
merged 36 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f251cf0
fix x-logo vendor extension
Jul 24, 2020
6fc3823
parse x-codeSamples from files
Jul 24, 2020
d3b7a3f
update swag init -h section in readme
Jul 24, 2020
52df97f
add atreugo to list of supported web frameworks
Jul 24, 2020
12fe750
update readme
Jul 24, 2020
690e4ed
update readme
Jul 24, 2020
c1754f2
Merge branch 'master' into vendor-extensions
easonlin404 Aug 3, 2020
b28be34
fix x-logo vendor extension
Jul 24, 2020
0fbd5fd
parse x-codeSamples from files
Jul 24, 2020
277826e
update swag init -h section in readme
Jul 24, 2020
b30fdae
add atreugo to list of supported web frameworks
Jul 24, 2020
e4e06c6
update readme
Jul 24, 2020
7675b2a
update readme
Jul 24, 2020
f08813c
fix linter errors
Aug 4, 2020
beb7622
Merge branch 'vendor-extensions' of github.com:Nerzal/swag into vendo…
Aug 4, 2020
cc26054
fix errors introduced by auto merge
Aug 4, 2020
42a0c89
Merge remote-tracking branch 'origin/master' into vendor-extensions
Aug 11, 2020
45fd0d5
go fmt'd
Aug 11, 2020
9480b7b
fix merge errors
Aug 11, 2020
a27a8c8
Merge remote-tracking branch 'origin/master' into vendor-extensions
Aug 12, 2020
280d30a
fix merge errors
Aug 12, 2020
84a3d81
setCodeExampleDirectory when instanciating a NewOperation
Aug 12, 2020
0fc262a
fix lint error
Aug 12, 2020
e6a10cc
Merge branch 'master' into vendor-extensions
Aug 19, 2020
b7843ef
fix linter warning and update dependencies
Aug 20, 2020
9f47519
add some error checks in tests
Aug 20, 2020
a9260b1
vendor extensions won't be saved with toLower names anymore
Aug 24, 2020
e1ecc4d
validate json when loading code samples from file
Aug 24, 2020
2118dec
remove unused method
Aug 24, 2020
519d484
add negative test for markdown files to make codecov happy
Aug 24, 2020
b0d0cec
Merge branch 'master' into vendor-extensions
easonlin404 Aug 29, 2020
69abb5a
fix tests
Aug 29, 2020
8f84ba8
Merge branch 'master' into vendor-extensions
easonlin404 Oct 4, 2020
748fe12
Merge remote-tracking branch 'origin/master' into vendor-extensions
Oct 4, 2020
0d45116
go mod tidied
Oct 4, 2020
a4fa218
Merge remote-tracking branch 'fork/vendor-extensions' into vendor-ext…
Oct 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,18 @@ USAGE:
swag init [command options] [arguments...]

OPTIONS:
--generalInfo value, -g value Go file path in which 'swagger general API Info' is written (default: "main.go")
--dir value, -d value Directory you want to parse (default: "./")
--exclude value Exclude directoies and files, comma separated
--propertyStrategy value, -p value Property Naming Strategy like snakecase,camelcase,pascalcase (default: "camelcase")
--output value, -o value Output directory for all the generated files(swagger.json, swagger.yaml and doc.go) (default: "./docs")
--parseVendor Parse go files in 'vendor' folder, disabled by default
--parseDependency Parse go files in outside dependency folder, disabled by default
--parseInternal Parse go files in internal packages, disabled by default
--generalInfo value, -g value Go file path in which 'swagger general API Info' is written (default: "main.go")
--dir value, -d value Directory you want to parse (default: "./")
--exclude value exclude directories and files when searching, comma separated
--propertyStrategy value, -p value Property Naming Strategy like snakecase,camelcase,pascalcase (default: "camelcase")
--output value, -o value Output directory for all the generated files(swagger.json, swagger.yaml and doc.go) (default: "./docs")
--parseVendor Parse go files in 'vendor' folder, disabled by default (default: false)
--parseDependency Parse go files in outside dependency folder, disabled by default (default: false)
--markdownFiles value, --md value Parse folder containing markdown files to use as description, disabled by default
--codeExampleFiles value, --cef value Parse folder containing code example files to use for the x-codeSamples extension, disabled by default
--parseInternal Parse go files in internal packages, disabled by default (default: false)
--generatedTime Generate timestamp at the top of docs.go, true by default (default: false)
--help, -h show help (default: false)
```

## Supported Web Frameworks
Expand All @@ -91,6 +95,7 @@ OPTIONS:
- [net/http](https://github.com/swaggo/http-swagger)
- [flamingo](https://github.com/i-love-flamingo/swagger)
- [fiber](https://github.com/arsmn/fiber-swagger)
- [atreugo](https://github.com/Nerzal/atreugo-swagger)

## How to use it with Gin

Expand Down Expand Up @@ -374,6 +379,7 @@ When a short string in your documentation is insufficient, or you need images, c
| header | Header in response that separated by spaces. `return code`,`{param type}`,`data type`,`comment` |
| router | Path definition that separated by spaces. `path`,`[httpMethod]` |
| x-name | The extension key, must be start by x- and take only json value. |
| x-codeSample | Optional Markdown usage. take `file` as parameter. This will then search for a file named like the summary in the given folder. |
| deprecated | Mark endpoint as deprecated. |


Expand Down
28 changes: 18 additions & 10 deletions cmd/swag/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
parseVendorFlag = "parseVendor"
parseDependencyFlag = "parseDependency"
markdownFilesFlag = "markdownFiles"
codeExampleFilesFlag = "codeExampleFiles"
parseInternal = "parseInternal"
generatedTimeFlag = "generatedTime"
)
Expand Down Expand Up @@ -66,6 +67,12 @@ var initFlags = []cli.Flag{
Value: "",
Usage: "Parse folder containing markdown files to use as description, disabled by default",
},
&cli.StringFlag{
Name: codeExampleFilesFlag,
Aliases: []string{"cef"},
Value: "",
Usage: "Parse folder containing code example files to use for the x-codeSamples extension, disabled by default",
},
&cli.BoolFlag{
Name: "parseInternal",
Usage: "Parse go files in internal packages, disabled by default",
Expand All @@ -86,16 +93,17 @@ func initAction(c *cli.Context) error {
}

return gen.New().Build(&gen.Config{
SearchDir: c.String(searchDirFlag),
Excludes: c.String(excludeFlag),
MainAPIFile: c.String(generalInfoFlag),
PropNamingStrategy: strategy,
OutputDir: c.String(outputFlag),
ParseVendor: c.Bool(parseVendorFlag),
ParseDependency: c.Bool(parseDependencyFlag),
MarkdownFilesDir: c.String(markdownFilesFlag),
ParseInternal: c.Bool(parseInternal),
GeneratedTime: c.Bool(generatedTimeFlag),
SearchDir: c.String(searchDirFlag),
Excludes: c.String(excludeFlag),
MainAPIFile: c.String(generalInfoFlag),
PropNamingStrategy: strategy,
OutputDir: c.String(outputFlag),
ParseVendor: c.Bool(parseVendorFlag),
ParseDependency: c.Bool(parseDependencyFlag),
MarkdownFilesDir: c.String(markdownFilesFlag),
ParseInternal: c.Bool(parseInternal),
GeneratedTime: c.Bool(generatedTimeFlag),
CodeExampleFilesDir: c.String(codeExampleFilesFlag),
})
}

Expand Down
3 changes: 3 additions & 0 deletions gen/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ type Config struct {

// GeneratedTime whether swag should generate the timestamp at the top of docs.go
GeneratedTime bool

// CodeExampleFilesDir used to find code example files, which can be used for x-codeSamples
CodeExampleFilesDir string
}

// Build builds swagger json file for given searchDir and mainAPIFile. Returns json
Expand Down
73 changes: 69 additions & 4 deletions operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"go/ast"
goparser "go/parser"
"go/token"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
Expand All @@ -24,7 +26,8 @@ type Operation struct {
Path string
spec.Operation

parser *Parser
parser *Parser
codeExampleFilesDir string
}

var mimeTypeAliases = map[string]string{
Expand All @@ -46,17 +49,34 @@ var mimeTypePattern = regexp.MustCompile("^[^/]+/[^/]+$")

// NewOperation creates a new Operation with default properties.
// map[int]Response
func NewOperation(parser *Parser) *Operation {
func NewOperation(parser *Parser, options ...func(*Operation)) *Operation {
if parser == nil {
parser = New()
}
return &Operation{

result := &Operation{
parser: parser,
HTTPMethod: "get",
Operation: spec.Operation{
OperationProps: spec.OperationProps{},
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{},
},
},
}

for _, option := range options {
option(result)
}

return result
}

// SetCodeExampleFilesDirectory sets the directory to search for codeExamples
func SetCodeExampleFilesDirectory(directoryPath string) func(*Operation) {
Nerzal marked this conversation as resolved.
Show resolved Hide resolved
return func(o *Operation) {
o.codeExampleFilesDir = directoryPath
}
}

// ParseComment parses comment for given comment string and returns error if error occurs.
Expand Down Expand Up @@ -101,13 +121,30 @@ func (operation *Operation) ParseComment(comment string, astFile *ast.File) erro
err = operation.ParseSecurityComment(lineRemainder)
case "@deprecated":
operation.Deprecate()
case "@x-codesamples":
err = operation.ParseCodeSample(attribute, commentLine, lineRemainder)
default:
err = operation.ParseMetadata(attribute, lowerAttribute, lineRemainder)
}

return err
}

// ParseDescriptionComment godoc
func (operation *Operation) ParseCodeSample(attribute, commentLine, lineRemainder string) error {
if lineRemainder == "file" {
data, err := getCodeExampleForSummary(operation.Summary, operation.codeExampleFilesDir)
if err != nil {
return err
}

operation.Extensions["x-codeSamples"] = string(data)
return nil
}

// Fallback into existing logic
return operation.ParseMetadata(attribute, strings.ToLower(attribute), lineRemainder)
}

// ParseDescriptionComment godoc
func (operation *Operation) ParseDescriptionComment(lineRemainder string) {
if operation.Description == "" {
Expand Down Expand Up @@ -847,3 +884,31 @@ func createParameter(paramType, description, paramName, schemaType string, requi
}
return parameter
}

func getCodeExampleForSummary(summaryName string, dirPath string) ([]byte, error) {
filesInfos, err := ioutil.ReadDir(dirPath)
if err != nil {
return nil, err
}

for _, fileInfo := range filesInfos {
if fileInfo.IsDir() {
continue
}
fileName := fileInfo.Name()

if !strings.Contains(fileName, ".json") {
continue
}

if strings.Contains(fileName, summaryName) {
fullPath := filepath.Join(dirPath, fileName)
commentInfo, err := ioutil.ReadFile(fullPath)
if err != nil {
return nil, fmt.Errorf("Failed to read code example file %s error: %s ", fullPath, err)
}
return commentInfo, nil
}
}
return nil, fmt.Errorf("Unable to find code example file for tag %s in the given directory", summaryName)
}
28 changes: 28 additions & 0 deletions operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1479,3 +1479,31 @@ func TestParseExtentions(t *testing.T) {
assert.Equal(t, expected, string(b))
}
}

func TestParseCodeSamples(t *testing.T) {
t.Run("Find sample by file", func(t *testing.T) {
comment := `@x-codeSamples file`
operation := NewOperation(nil, SetCodeExampleFilesDirectory("testdata/code_examples"))
operation.Summary = "example"

err := operation.ParseComment(comment, nil)
assert.NoError(t, err, "no error should be thrown")

b, _ := json.MarshalIndent(operation, "", " ")

expected := `{
"summary": "example",
"x-codeSamples": "{\n \"lang\": \"JavaScript\",\n \"source\": \"console.log('Hello World');\"\n}"
}`
assert.Equal(t, expected, string(b))
})

t.Run("Example file not found", func(t *testing.T) {
comment := `@x-codeSamples file`
operation := NewOperation(nil, SetCodeExampleFilesDirectory("testdata/code_examples"))
operation.Summary = "exampel"

err := operation.ParseComment(comment, nil)
assert.Error(t, err, "error was expected, as file does not exist")
})
}
10 changes: 9 additions & 1 deletion parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ func New(options ...func(*Parser)) *Parser {
Contact: &spec.ContactInfo{},
License: &spec.License{},
},
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{},
},
},
Paths: &spec.Paths{
Paths: make(map[string]spec.PathItem),
Expand Down Expand Up @@ -358,7 +361,12 @@ func (parser *Parser) ParseGeneralAPIInfo(mainAPIFile string) error {
if err := json.Unmarshal([]byte(split[1]), &valueJSON); err != nil {
return fmt.Errorf("annotation %s need a valid json value", attribute)
}
parser.swagger.AddExtension(extensionName, valueJSON)

if strings.Contains(extensionName, "logo") {
parser.swagger.Info.Extensions.Add(extensionName, valueJSON)
} else {
parser.swagger.AddExtension(extensionName, valueJSON)
}
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ func TestParser_ParseGeneralApiInfo(t *testing.T) {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
"version": "1.0",
"x-logo": {
"altText": "Petstore logo",
"backgroundColor": "#FFFFFF",
"url": "https://redocly.github.io/redoc/petstore-logo.png"
}
},
"host": "petstore.swagger.io",
"basePath": "/v2",
Expand Down
13 changes: 13 additions & 0 deletions testdata/code_examples/api/api1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package api

import (
_ "github.com/swaggo/swag/testdata/conflict_name/model"
"net/http"
)

// @Description Check if Health of service it's OK!
// @Router /health [get]
// @x-codeSamples file
func Get1(w http.ResponseWriter, r *http.Request) {

}
4 changes: 4 additions & 0 deletions testdata/code_examples/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"lang": "JavaScript",
"source": "console.log('Hello World');"
}
8 changes: 8 additions & 0 deletions testdata/code_examples/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

// @title Swag test
// @version 1.0
// @description test for conflict name
func main() {

}
1 change: 1 addition & 0 deletions testdata/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ package main

// @x-google-endpoints [{"name":"name.endpoints.environment.cloud.goog","allowCors":true}]
// @x-google-marks "marks values"
// @x-logo {"url":"https://redocly.github.io/redoc/petstore-logo.png", "altText": "Petstore logo", "backgroundColor": "#FFFFFF"}

func main() {}