From 5fcb6d5ce987b08c35240cd7de688a40dacd4659 Mon Sep 17 00:00:00 2001 From: tcarreira Date: Mon, 13 Apr 2020 23:41:31 -0300 Subject: [PATCH 1/2] parseField skips not-exported (encoding/json-like) --- parser.go | 5 + parser_test.go | 170 ++++++++++++++++++++++ testdata/non_exported_json_fields/main.go | 44 ++++++ testdata/simple2/web/handler.go | 4 +- 4 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 testdata/non_exported_json_fields/main.go diff --git a/parser.go b/parser.go index bb983355e..eab7776bc 100644 --- a/parser.go +++ b/parser.go @@ -1165,6 +1165,11 @@ func (parser *Parser) parseField(pkgName string, field *ast.Field) (*structField return &structField{name: ""}, nil } + // Skip non-exported fields. + if !ast.IsExported(field.Names[0].Name) { + return &structField{name: ""}, nil + } + structField := &structField{ name: field.Names[0].Name, schemaType: prop.SchemaType, diff --git a/parser_test.go b/parser_test.go index a06146712..d09de8928 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1947,6 +1947,176 @@ func TestParseStructComment(t *testing.T) { assert.Equal(t, expected, string(b)) } +func TestParseNonExportedJSONFields(t *testing.T) { + + //region declaration + expected := `{ + "swagger": "2.0", + "info": { + "description": "This is a sample server.", + "title": "Swagger Example API", + "contact": {}, + "license": {}, + "version": "1.0" + }, + "host": "localhost:4000", + "basePath": "/api", + "paths": { + "/posts/{post_id}": { + "get": { + "description": "get string by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Add a new pet to the store", + "parameters": [ + { + "type": "integer", + "format": "int64", + "description": "Some ID", + "name": "post_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, + "/so-something": { + "get": { + "description": "Does something, but internal (non-exported) fields inside a struct won't be marshaled into JSON", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "Call DoSomething", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + } + }, + "definitions": { + "main.MyStruct": { + "type": "object", + "properties": { + "data": { + "description": "Post data", + "type": "object", + "properties": { + "name": { + "description": "Post tag", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "description": "Post name", + "type": "string", + "example": "poti" + } + } + }, + "web.APIError": { + "type": "object", + "properties": { + "createdAt": { + "description": "Error time", + "type": "string" + }, + "error": { + "description": "Error an Api error", + "type": "string" + }, + "errorCtx": { + "description": "Error ` + "`" + `context` + "`" + ` tick comment", + "type": "string" + }, + "errorNo": { + "description": "Error ` + "`" + `number` + "`" + ` tick comment", + "type": "integer" + } + } + }, + "web.Post": { + "type": "object", + "properties": { + "data": { + "description": "Post data", + "type": "object", + "properties": { + "name": { + "description": "Post tag", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "description": "Post name", + "type": "string", + "example": "poti" + } + } + } + } +}` + //endregion declaration + + searchDir := "testdata/non_exported_json_fields" + mainAPIFile := "main.go" + p := New() + err := p.ParseAPI(searchDir, mainAPIFile) + assert.NoError(t, err) + b, _ := json.MarshalIndent(p.swagger, "", " ") + ioutil.WriteFile("/tmp/test1", b, 0644) + assert.Equal(t, expected, string(b)) +} + func TestParsePetApi(t *testing.T) { expected := `{ "schemes": [ diff --git a/testdata/non_exported_json_fields/main.go b/testdata/non_exported_json_fields/main.go new file mode 100644 index 000000000..ed641c38d --- /dev/null +++ b/testdata/non_exported_json_fields/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "github.com/gin-gonic/gin" +) + +type MyStruct struct { + ID int `json:"id" example:"1" format:"int64"` + // Post name + Name string `json:"name" example:"poti"` + // Post data + Data struct { + // Post tag + Tag []string `json:"name"` + } `json:"data"` + // not-exported variable, for internal use only, not marshaled + internal1 string + internal2 int + internal3 bool + internal4 struct { + NestedInternal string + } +} + +// @Summary Call DoSomething +// @Description Does something, but internal (non-exported) fields inside a struct won't be marshaled into JSON +// @Accept json +// @Produce json +// @Success 200 {string} MyStruct +// @Router /so-something [get] +func DoSomething(c *gin.Context) { + //write your code +} + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server. +// @host localhost:4000 +// @basePath /api +func main() { + r := gin.New() + r.GET("/do-something", api.DoSomething) + r.Run() +} diff --git a/testdata/simple2/web/handler.go b/testdata/simple2/web/handler.go index d9a568692..1ab61b920 100644 --- a/testdata/simple2/web/handler.go +++ b/testdata/simple2/web/handler.go @@ -53,8 +53,8 @@ type Pet struct { Hidden string `json:"-"` UUID uuid.UUID Decimal decimal.Decimal - customString CustomString - customStringArr []CustomString + CustomString CustomString + CustomStringArr []CustomString NullInt sql.NullInt64 `swaggertype:"integer"` Coeffs []big.Float `swaggertype:"array,number"` Birthday TimestampTime `swaggertype:"primitive,integer"` From 6fb2a2d77291382c8483e201dc51cd0b32060c92 Mon Sep 17 00:00:00 2001 From: tcarreira Date: Tue, 14 Apr 2020 00:02:53 -0300 Subject: [PATCH 2/2] fix TestParseNonExportedJSONFields --- parser_test.go | 95 ------------------------------ testdata/json_field_string/main.go | 39 ++++++++++++ 2 files changed, 39 insertions(+), 95 deletions(-) create mode 100644 testdata/json_field_string/main.go diff --git a/parser_test.go b/parser_test.go index d09de8928..7baa835c0 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1948,8 +1948,6 @@ func TestParseStructComment(t *testing.T) { } func TestParseNonExportedJSONFields(t *testing.T) { - - //region declaration expected := `{ "swagger": "2.0", "info": { @@ -1962,48 +1960,6 @@ func TestParseNonExportedJSONFields(t *testing.T) { "host": "localhost:4000", "basePath": "/api", "paths": { - "/posts/{post_id}": { - "get": { - "description": "get string by ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "summary": "Add a new pet to the store", - "parameters": [ - { - "type": "integer", - "format": "int64", - "description": "Some ID", - "name": "post_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "string" - } - }, - "400": { - "description": "We need ID!!", - "schema": { - "$ref": "#/definitions/web.APIError" - } - }, - "404": { - "description": "Can not find ID", - "schema": { - "$ref": "#/definitions/web.APIError" - } - } - } - } - }, "/so-something": { "get": { "description": "Does something, but internal (non-exported) fields inside a struct won't be marshaled into JSON", @@ -2053,59 +2009,9 @@ func TestParseNonExportedJSONFields(t *testing.T) { "example": "poti" } } - }, - "web.APIError": { - "type": "object", - "properties": { - "createdAt": { - "description": "Error time", - "type": "string" - }, - "error": { - "description": "Error an Api error", - "type": "string" - }, - "errorCtx": { - "description": "Error ` + "`" + `context` + "`" + ` tick comment", - "type": "string" - }, - "errorNo": { - "description": "Error ` + "`" + `number` + "`" + ` tick comment", - "type": "integer" - } - } - }, - "web.Post": { - "type": "object", - "properties": { - "data": { - "description": "Post data", - "type": "object", - "properties": { - "name": { - "description": "Post tag", - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "id": { - "type": "integer", - "format": "int64", - "example": 1 - }, - "name": { - "description": "Post name", - "type": "string", - "example": "poti" - } - } } } }` - //endregion declaration searchDir := "testdata/non_exported_json_fields" mainAPIFile := "main.go" @@ -2113,7 +2019,6 @@ func TestParseNonExportedJSONFields(t *testing.T) { err := p.ParseAPI(searchDir, mainAPIFile) assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") - ioutil.WriteFile("/tmp/test1", b, 0644) assert.Equal(t, expected, string(b)) } diff --git a/testdata/json_field_string/main.go b/testdata/json_field_string/main.go new file mode 100644 index 000000000..04515dccb --- /dev/null +++ b/testdata/json_field_string/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/gin-gonic/gin" +) + +type MyStruct struct { + ID int `json:"id" example:"1" format:"int64"` + // Post name + Name string `json:"name" example:"poti"` + // Post data + Data struct { + // Post tag + Tag []string `json:"name"` + } `json:"data"` + // Integer represented by a string + MyInt int `json:"myint,string"` +} + +// @Summary Call DoSomething +// @Description Does something, but internal (non-exported) fields inside a struct won't be marshaled into JSON +// @Accept json +// @Produce json +// @Success 200 {string} MyStruct +// @Router /so-something [get] +func DoSomething(c *gin.Context) { + //write your code +} + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server. +// @host localhost:4000 +// @basePath /api +func main() { + r := gin.New() + r.GET("/do-something", api.DoSomething) + r.Run() +}