├── .gitignore ├── .travis.gofmt.sh ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── common_types.go ├── common_types_test.go ├── definition.go ├── definition_test.go ├── doc.go ├── entities.go ├── entities_test.go ├── example_test.go ├── generator.go ├── generator_singleton.go ├── generator_test.go ├── glide.lock ├── glide.yaml ├── helper.go ├── helper_test.go ├── parser.go ├── parser_test.go ├── sample └── sample.go └── testdata ├── test_JSON-RPC.json └── test_REST.json /.gitignore: -------------------------------------------------------------------------------- 1 | ### Go template 2 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 3 | *.o 4 | *.a 5 | *.so 6 | 7 | # Folders 8 | _obj 9 | _test 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | 27 | # Created by .ignore support plugin (hsz.mobi) 28 | 29 | .vscode 30 | .idea 31 | tmp 32 | testdata/test_REST_last_run.json 33 | testdata/test_JSON-RPC_last_run.json 34 | vendor/* 35 | -------------------------------------------------------------------------------- /.travis.gofmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -n "$(gofmt -l .)" ]; then 4 | echo "Go code is not formatted:" 5 | gofmt -d -e . 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7.x 5 | - 1.8.x 6 | - 1.9.x 7 | - 1.10.x 8 | - tip 9 | 10 | before_install: 11 | - go get github.com/mattn/goveralls 12 | - go get golang.org/x/tools/cmd/cover 13 | - go get github.com/golang/lint/golint 14 | 15 | install: 16 | - go get -t -v ./... 17 | 18 | script: 19 | - ./.travis.gofmt.sh 20 | - $HOME/gopath/bin/golint -set_exit_status . 21 | - go vet 22 | - go test -v -cover -race -coverprofile coverage.out 23 | - $HOME/gopath/bin/goveralls -coverprofile coverage.out -service travis-ci 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v0.3.4](https://github.com/lazada/swgen/tree/v0.3.4) (2017-10-03) 4 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.3.3...v0.3.4) 5 | 6 | **Fixed bugs:** 7 | 8 | - Make generator threadsafe again [\#25](https://github.com/lazada/swgen/pull/25) ([THE108](https://github.com/THE108)) 9 | 10 | ## [v0.3.3](https://github.com/lazada/swgen/tree/v0.3.3) (2017-09-12) 11 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.3.2...v0.3.3) 12 | 13 | **Fixed bugs:** 14 | 15 | - Fixes \#23 Infinite recursion with custom type mapping [\#24](https://github.com/lazada/swgen/pull/24) ([vearutop](https://github.com/vearutop)) 16 | 17 | ## [v0.3.2](https://github.com/lazada/swgen/tree/v0.3.2) (2017-07-19) 18 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.3.1...v0.3.2) 19 | 20 | **Fixed bugs:** 21 | 22 | - Structures in different packages with same name producing single definition [\#21](https://github.com/lazada/swgen/issues/21) 23 | - Structures in different packages with same name producing single definition [\#22](https://github.com/lazada/swgen/pull/22) ([vearutop](https://github.com/vearutop)) 24 | 25 | ## [v0.3.1](https://github.com/lazada/swgen/tree/v0.3.1) (2017-07-14) 26 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.3.0...v0.3.1) 27 | 28 | **Implemented enhancements:** 29 | 30 | - Removed redundant mutexes from swgen.Generator [\#20](https://github.com/lazada/swgen/pull/20) ([sb-lazada](https://github.com/sb-lazada)) 31 | 32 | ## [v0.3.0](https://github.com/lazada/swgen/tree/v0.3.0) (2017-07-13) 33 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.2.1...v0.3.0) 34 | 35 | **Implemented enhancements:** 36 | 37 | - Security controls [\#19](https://github.com/lazada/swgen/pull/19) ([vearutop](https://github.com/vearutop)) 38 | - Add testing reference to empty struct [\#18](https://github.com/lazada/swgen/pull/18) ([vearutop](https://github.com/vearutop)) 39 | - Handling of anonymous structs and nested structs in interface{} fields [\#16](https://github.com/lazada/swgen/pull/16) ([sb-lazada](https://github.com/sb-lazada)) 40 | 41 | ## [v0.2.1](https://github.com/lazada/swgen/tree/v0.2.1) (2017-05-30) 42 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.2.0...v0.2.1) 43 | 44 | **Fixed bugs:** 45 | 46 | - Missing go type reflection from anonymous properties [\#14](https://github.com/lazada/swgen/pull/14) ([vearutop](https://github.com/vearutop)) 47 | 48 | ## [v0.2.0](https://github.com/lazada/swgen/tree/v0.2.0) (2017-05-23) 49 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.1.1...v0.2.0) 50 | 51 | **Implemented enhancements:** 52 | 53 | - Reflect original go types and swagger compliance [\#13](https://github.com/lazada/swgen/pull/13) ([vearutop](https://github.com/vearutop)) 54 | - Use func SwgenDefinition if destination mapping type implements IDefinition interface. [\#12](https://github.com/lazada/swgen/pull/12) ([ngdinhtoan](https://github.com/ngdinhtoan)) 55 | - Support cross-origin HTTP request \(CORS\) [\#11](https://github.com/lazada/swgen/pull/11) ([ngdinhtoan](https://github.com/ngdinhtoan)) 56 | - Run go vet when build on travis-ci [\#10](https://github.com/lazada/swgen/pull/10) ([ngdinhtoan](https://github.com/ngdinhtoan)) 57 | - Get rid of hard code check type name of struct to create object schema, use interface IDefinition or swgen\_type tag instead. [\#9](https://github.com/lazada/swgen/pull/9) ([ngdinhtoan](https://github.com/ngdinhtoan)) 58 | 59 | ## [v0.1.1](https://github.com/lazada/swgen/tree/v0.1.1) (2017-05-10) 60 | [Full Changelog](https://github.com/lazada/swgen/compare/v0.1.0...v0.1.1) 61 | 62 | **Implemented enhancements:** 63 | 64 | - Clean up code and some minor improvements. [\#7](https://github.com/lazada/swgen/pull/7) ([ngdinhtoan](https://github.com/ngdinhtoan)) 65 | 66 | **Fixed bugs:** 67 | 68 | - Fixed bug in using switch case statement. [\#8](https://github.com/lazada/swgen/pull/8) ([ngdinhtoan](https://github.com/ngdinhtoan)) 69 | 70 | **Closed issues:** 71 | 72 | - Omit empty `host` [\#4](https://github.com/lazada/swgen/issues/4) 73 | 74 | ## [v0.1.0](https://github.com/lazada/swgen/tree/v0.1.0) (2017-04-20) 75 | **Merged pull requests:** 76 | 77 | - \#4 omit empty `host` [\#5](https://github.com/lazada/swgen/pull/5) ([vearutop](https://github.com/vearutop)) 78 | - Check if code is formatted while building. [\#3](https://github.com/lazada/swgen/pull/3) ([ngdinhtoan](https://github.com/ngdinhtoan)) 79 | - Processing `json.RawMessage` as `{}` [\#2](https://github.com/lazada/swgen/pull/2) ([vearutop](https://github.com/vearutop)) 80 | - Added travis-ci [\#1](https://github.com/lazada/swgen/pull/1) ([ngdinhtoan](https://github.com/ngdinhtoan)) 81 | 82 | 83 | 84 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Lazada Tech Hub 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swagger Generator (swgen) 2 | 3 | [![Build Status](https://travis-ci.org/lazada/swgen.svg?branch=master)](https://travis-ci.org/lazada/swgen) 4 | [![Coverage Status](https://coveralls.io/repos/github/lazada/swgen/badge.svg?branch=master)](https://coveralls.io/github/lazada/swgen?branch=master) 5 | [![GoDoc](https://godoc.org/github.com/lazada/swgen?status.svg)](https://godoc.org/github.com/lazada/swgen) 6 | 7 | Swagger Generator is a library which helps to generate [Swagger Specification](http://swagger.io/specification/) in JSON format on-the-fly. 8 | 9 | ## Installation 10 | 11 | You can use `go get` to install the `swgen` package 12 | 13 | go get github.com/lazada/swgen 14 | 15 | Then import it into your own code 16 | 17 | ```go 18 | import "github.com/lazada/swgen" 19 | ``` 20 | 21 | ## Example 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "fmt" 28 | 29 | "github.com/lazada/swgen" 30 | ) 31 | 32 | // PetsRequest defines all params for /pets request 33 | type PetsRequest struct { 34 | Tags []string `schema:"tags" in:"query" required:"-" description:"tags to filter by"` 35 | Limit int32 `schema:"limit" in:"query" required:"-" description:"maximum number of results to return"` 36 | } 37 | 38 | // Pet contains information of a pet 39 | type Pet struct { 40 | ID int64 `json:"id"` 41 | Name string `json:"name"` 42 | Tag string `json:"tag"` 43 | } 44 | 45 | func main() { 46 | gen := swgen.NewGenerator() 47 | gen.SetHost("petstore.swagger.io").SetBasePath("/api") 48 | gen.SetInfo("Swagger Petstore (Simple)", "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", "http://helloreverb.com/terms/", "2.0") 49 | gen.SetLicense("MIT", "http://opensource.org/licenses/MIT") 50 | gen.SetContact("Swagger API team", "http://swagger.io", "foo@example.com") 51 | gen.AddSecurityDefinition("BasicAuth", swgen.SecurityDef{Type: swgen.SecurityBasicAuth}) 52 | 53 | pathInf := swgen.PathItemInfo{ 54 | Path: "/pets", 55 | Method: "GET", 56 | Title: "findPets", 57 | Description: "Returns all pets from the system that the user has access to", 58 | Tag: "v1", 59 | Deprecated: false, 60 | Security: []string{"BasicAuth"}, 61 | } 62 | pathInf.AddExtendedField("x-example", "example") 63 | 64 | gen.SetPathItem( 65 | pathInf, 66 | PetsRequest{}, // request object 67 | nil, // body data if any 68 | []Pet{}, // response object 69 | ) 70 | 71 | // extended field 72 | gen.AddExtendedField("x-uppercase-version", true) 73 | 74 | docData, _ := gen.GenDocument() 75 | fmt.Println(string(docData)) 76 | 77 | // output: 78 | // {"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"2.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http","https"],"paths":{"/pets":{"get":{"tags":["v1"],"summary":"findPets","description":"Returns all pets from the system that the user has access to","parameters":[{"name":"tags","in":"query","type":"array","items":{"type":"string"},"collectionFormat":"multi","description":"tags to filter by"},{"name":"limit","in":"query","type":"integer","format":"int32","description":"maximum number of results to return"}],"responses":{"200":{"description":"request success","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}}},"security":{"BasicAuth":[]},"x-example":"example"}}},"definitions":{"Pet":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"securityDefinitions":{"BasicAuth":{"type":"basic"}},"x-uppercase-version":true} 79 | } 80 | ``` 81 | 82 | ### Generate swagger docs for RPC service 83 | 84 | You even can use `swgen` to generate swagger document for a JSON-RPC service as below example 85 | 86 | ```go 87 | package main 88 | 89 | import ( 90 | "fmt" 91 | 92 | "github.com/lazada/swgen" 93 | ) 94 | 95 | const ( 96 | // XServiceType is a swagger vendor extension 97 | XServiceType = `x-service-type` 98 | // XAttachVersionToHead is a swagger vendor extension 99 | XAttachVersionToHead = `x-attach-version-to-head` 100 | ) 101 | 102 | // Pet contains information of a pet 103 | type Pet struct { 104 | ID int64 `json:"id"` 105 | Name string `json:"name"` 106 | Tag string `json:"tag"` 107 | } 108 | 109 | func main() { 110 | gen := swgen.NewGenerator() 111 | gen.SetHost("petstore.swagger.io") 112 | gen.SetBasePath("/rpc") // set JSON-RPC path 113 | gen.SetInfo("Swagger Petstore (Simple)", "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", "http://helloreverb.com/terms/", "2.0") 114 | gen.SetLicense("MIT", "http://opensource.org/licenses/MIT") 115 | gen.SetContact("Swagger API team", "http://swagger.io", "foo@example.com") 116 | 117 | // set service type is JSON-RPC 118 | gen.AddExtendedField(XServiceType, swgen.ServiceTypeJSONRPC) 119 | gen.AddExtendedField(XAttachVersionToHead, false) 120 | 121 | pathInf := swgen.PathItemInfo{ 122 | Path: "addPet", // in JSON-RPC, use name of method for Path 123 | Method: "POST", // JSON-RPC always use POST method 124 | Title: "Add new Pet", 125 | Description: "Add a new pet to the store", 126 | Tag: "v1", 127 | Deprecated: false, 128 | } 129 | 130 | gen.SetPathItem( 131 | pathInf, 132 | nil, // no param, all in body 133 | Pet{}, // body data if any 134 | Pet{}, // response object 135 | ) 136 | 137 | docData, _ := gen.GenDocument() 138 | fmt.Println(string(docData)) 139 | 140 | // output: 141 | // {"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"2.0"},"host":"petstore.swagger.io","basePath":"/rpc","schemes":["http","https"],"paths":{"addPet":{"post":{"tags":["v1"],"summary":"Add new Pet","description":"Add a new pet to the store","parameters":[{"name":"body","in":"body","schema":{"$ref":"#/definitions/Pet"},"required":true}],"responses":{"200":{"description":"request success","schema":{"$ref":"#/definitions/Pet"}}}}}},"definitions":{"Pet":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"x-attach-version-to-head":false,"x-service-type":"json-rpc"} 142 | } 143 | ``` 144 | 145 | ## License 146 | 147 | Distributed under the Apache License, version 2.0. 148 | Please see license file in code for more details. 149 | -------------------------------------------------------------------------------- /common_types.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | type commonName string 4 | 5 | const ( 6 | // CommonNameInteger data type is integer, format int32 (signed 32 bits) 7 | CommonNameInteger commonName = "integer" 8 | // CommonNameLong data type is integer, format int64 (signed 64 bits) 9 | CommonNameLong commonName = "long" 10 | // CommonNameFloat data type is number, format float 11 | CommonNameFloat commonName = "float" 12 | // CommonNameDouble data type is number, format double 13 | CommonNameDouble commonName = "double" 14 | // CommonNameString data type is string 15 | CommonNameString commonName = "string" 16 | // CommonNameByte data type is string, format byte (base64 encoded characters) 17 | CommonNameByte commonName = "byte" 18 | // CommonNameBinary data type is string, format binary (any sequence of octets) 19 | CommonNameBinary commonName = "binary" 20 | // CommonNameBoolean data type is boolean 21 | CommonNameBoolean commonName = "boolean" 22 | // CommonNameDate data type is string, format date (As defined by full-date - RFC3339) 23 | CommonNameDate commonName = "date" 24 | // CommonNameDateTime data type is string, format date-time (As defined by date-time - RFC3339) 25 | CommonNameDateTime commonName = "dateTime" 26 | // CommonNamePassword data type is string, format password 27 | CommonNamePassword commonName = "password" 28 | ) 29 | 30 | type typeFormat struct { 31 | Type string 32 | Format string 33 | } 34 | 35 | var commonNamesMap = map[commonName]typeFormat{ 36 | CommonNameInteger: {"integer", "int32"}, 37 | CommonNameLong: {"integer", "int64"}, 38 | CommonNameFloat: {"number", "float"}, 39 | CommonNameDouble: {"number", "double"}, 40 | CommonNameString: {"string", ""}, 41 | CommonNameByte: {"string", "byte"}, 42 | CommonNameBinary: {"string", "binary"}, 43 | CommonNameBoolean: {"boolean", ""}, 44 | CommonNameDate: {"string", "date"}, 45 | CommonNameDateTime: {"string", "date-time"}, 46 | CommonNamePassword: {"string", "password"}, 47 | } 48 | 49 | func isCommonName(typeName string) (ok bool) { 50 | _, ok = commonNamesMap[commonName(typeName)] 51 | return 52 | } 53 | 54 | // SchemaFromCommonName create SchemaObj from common name of data types 55 | // supported types: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types 56 | func SchemaFromCommonName(name commonName) SchemaObj { 57 | data, ok := commonNamesMap[name] 58 | if ok { 59 | return SchemaObj{ 60 | Type: data.Type, 61 | Format: data.Format, 62 | } 63 | } 64 | 65 | return SchemaObj{ 66 | Type: string(name), 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /common_types_test.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import "testing" 4 | 5 | func TestSchemaFromCommonName(t *testing.T) { 6 | so := SchemaFromCommonName(CommonNameInteger) 7 | assertTrue(so.Type == "integer", t) 8 | assertTrue(so.Format == "int32", t) 9 | 10 | so = SchemaFromCommonName("file") 11 | assertTrue(so.Type == "file", t) 12 | assertTrue(so.Format == "", t) 13 | } 14 | -------------------------------------------------------------------------------- /definition.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | // Definition is a helper that implements interface IDefinition 4 | type Definition struct { 5 | SchemaObj 6 | TypeName string 7 | } 8 | 9 | // SwgenDefinition return type name and definition that was set 10 | func (s Definition) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 11 | typeName = s.TypeName 12 | typeDef = s.SchemaObj 13 | return 14 | } 15 | -------------------------------------------------------------------------------- /definition_test.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDefinition(t *testing.T) { 8 | var obj IDefinition 9 | obj = Definition{ 10 | TypeName: "MyName", 11 | SchemaObj: SchemaObj{Type: "integer", Format: "int64"}, 12 | } 13 | 14 | typeName, _, err := obj.SwgenDefinition() 15 | assertTrue(typeName == "MyName", t) 16 | assertTrue(err == nil, t) 17 | } 18 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package swgen (Swagger Generator) is a library which helps to generate [Swagger Specification](http://swagger.io/specification/) in JSON format on-the-fly. 2 | package swgen 3 | -------------------------------------------------------------------------------- /entities.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | ) 7 | 8 | // ServiceType data type for type of your service 9 | type ServiceType string 10 | 11 | const ( 12 | // ServiceTypeRest define service type for RESTful service 13 | ServiceTypeRest ServiceType = "rest" 14 | // ServiceTypeJSONRPC define service type for JSON-RPC service 15 | ServiceTypeJSONRPC ServiceType = "json-rpc" 16 | ) 17 | 18 | // Document represent for a document object of swagger data 19 | // see http://swagger.io/specification/ 20 | type Document struct { 21 | Version string `json:"swagger"` // Specifies the Swagger Specification version being used 22 | Info InfoObj `json:"info"` // Provides metadata about the API 23 | Host string `json:"host,omitempty"` // The host (name or ip) serving the API 24 | BasePath string `json:"basePath,omitempty"` // The base path on which the API is served, which is relative to the host 25 | Schemes []string `json:"schemes,omitempty"` // Values MUST be from the list: "http", "https", "ws", "wss" 26 | Paths map[string]PathItem `json:"paths"` // The available paths and operations for the API 27 | Definitions map[string]SchemaObj `json:"definitions"` // An object to hold data types produced and consumed by operations 28 | SecurityDefinitions map[string]SecurityDef `json:"securityDefinitions,omitempty"` // An object to hold available security mechanisms 29 | additionalData 30 | } 31 | 32 | type _Document Document 33 | 34 | // MarshalJSON marshal Document with additionalData inlined 35 | func (s Document) MarshalJSON() ([]byte, error) { 36 | return s.marshalJSONWithStruct(_Document(s)) 37 | } 38 | 39 | // InfoObj provides metadata about the API 40 | type InfoObj struct { 41 | Title string `json:"title"` // The title of the application 42 | Description string `json:"description"` 43 | TermsOfService string `json:"termsOfService"` 44 | Contact ContactObj `json:"contact"` 45 | License LicenseObj `json:"license"` 46 | Version string `json:"version"` 47 | } 48 | 49 | // ContactObj contains contact information for the exposed API 50 | type ContactObj struct { 51 | Name string `json:"name"` 52 | URL string `json:"url,omitempty"` 53 | Email string `json:"email,omitempty"` 54 | } 55 | 56 | // LicenseObj license information for the exposed API 57 | type LicenseObj struct { 58 | Name string `json:"name"` 59 | URL string `json:"url,omitempty"` 60 | } 61 | 62 | // PathItem describes the operations available on a single path 63 | // see http://swagger.io/specification/#pathItemObject 64 | type PathItem struct { 65 | Ref string `json:"$ref,omitempty"` 66 | Get *OperationObj `json:"get,omitempty"` 67 | Put *OperationObj `json:"put,omitempty"` 68 | Post *OperationObj `json:"post,omitempty"` 69 | Delete *OperationObj `json:"delete,omitempty"` 70 | Options *OperationObj `json:"options,omitempty"` 71 | Head *OperationObj `json:"head,omitempty"` 72 | Patch *OperationObj `json:"patch,omitempty"` 73 | Params *ParamObj `json:"parameters,omitempty"` 74 | } 75 | 76 | // HasMethod returns true if in path item already have operation for given method 77 | func (pi PathItem) HasMethod(method string) bool { 78 | switch strings.ToUpper(method) { 79 | case "GET": 80 | return pi.Get != nil 81 | case "POST": 82 | return pi.Post != nil 83 | case "PUT": 84 | return pi.Put != nil 85 | case "DELETE": 86 | return pi.Delete != nil 87 | case "OPTIONS": 88 | return pi.Options != nil 89 | case "HEAD": 90 | return pi.Head != nil 91 | case "PATCH": 92 | return pi.Patch != nil 93 | } 94 | 95 | return false 96 | } 97 | 98 | type securityType string 99 | 100 | const ( 101 | // SecurityBasicAuth is a HTTP Basic Authentication security type 102 | SecurityBasicAuth securityType = "basic" 103 | // SecurityAPIKey is an API key security type 104 | SecurityAPIKey securityType = "apiKey" 105 | // SecurityOAuth2 is an OAuth2 security type 106 | SecurityOAuth2 securityType = "oauth2" 107 | ) 108 | 109 | type apiKeyIn string 110 | 111 | const ( 112 | // APIKeyInHeader defines API key in header 113 | APIKeyInHeader apiKeyIn = "header" 114 | // APIKeyInQuery defines API key in query parameter 115 | APIKeyInQuery apiKeyIn = "query" 116 | ) 117 | 118 | type oauthFlow string 119 | 120 | const ( 121 | // Oauth2AccessCode is access code Oauth2 flow 122 | Oauth2AccessCode oauthFlow = "accessCode" 123 | // Oauth2Application is application Oauth2 flow 124 | Oauth2Application oauthFlow = "application" 125 | // Oauth2Implicit is implicit Oauth2 flow 126 | Oauth2Implicit oauthFlow = "implicit" 127 | // Oauth2Password is password Oauth2 flow 128 | Oauth2Password oauthFlow = "password" 129 | ) 130 | 131 | // SecurityDef holds security definition 132 | type SecurityDef struct { 133 | Type securityType `json:"type"` 134 | 135 | // apiKey properties 136 | In apiKeyIn `json:"in,omitempty"` 137 | Name string `json:"name,omitempty"` // Example: X-API-Key 138 | 139 | // oauth2 properties 140 | Flow oauthFlow `json:"flow,omitempty"` 141 | AuthorizationURL string `json:"authorizationUrl,omitempty"` // Example: https://example.com/oauth/authorize 142 | TokenURL string `json:"tokenUrl,omitempty"` // Example: https://example.com/oauth/token 143 | Scopes map[string]string `json:"scopes,omitempty"` // Example: {"read": "Grants read access", "write": "Grants write access"} 144 | } 145 | 146 | // PathItemInfo some basic information of a path item and operation object 147 | type PathItemInfo struct { 148 | Path string 149 | Method string 150 | Title string 151 | Description string 152 | Tag string 153 | Deprecated bool 154 | 155 | Security []string // Names of security definitions 156 | SecurityOAuth2 map[string][]string // Map of names of security definitions to required scopes 157 | 158 | additionalData 159 | } 160 | 161 | // Enum can be use for sending Enum data that need validate 162 | type Enum struct { 163 | Enum []interface{} `json:"enum,omitempty"` 164 | EnumNames []string `json:"x-enum-names,omitempty"` 165 | } 166 | 167 | type enumer interface { 168 | // GetEnumSlices return the const-name pair slice 169 | GetEnumSlices() ([]interface{}, []string) 170 | } 171 | 172 | // OperationObj describes a single API operation on a path 173 | // see http://swagger.io/specification/#operationObject 174 | type OperationObj struct { 175 | Tags []string `json:"tags,omitempty"` 176 | Summary string `json:"summary"` // like a title, a short summary of what the operation does (120 chars) 177 | Description string `json:"description"` // A verbose explanation of the operation behavior 178 | Parameters []ParamObj `json:"parameters,omitempty"` 179 | Responses Responses `json:"responses"` 180 | Security []map[string][]string `json:"security,omitempty"` 181 | Deprecated bool `json:"deprecated,omitempty"` 182 | additionalData 183 | } 184 | 185 | type _OperationObj OperationObj 186 | 187 | // MarshalJSON marshal OperationObj with additionalData inlined 188 | func (o OperationObj) MarshalJSON() ([]byte, error) { 189 | return o.marshalJSONWithStruct(_OperationObj(o)) 190 | } 191 | 192 | // ParamObj describes a single operation parameter 193 | // see http://swagger.io/specification/#parameterObject 194 | type ParamObj struct { 195 | Ref string `json:"$ref,omitempty"` 196 | Name string `json:"name"` 197 | In string `json:"in"` // Possible values are "query", "header", "path", "formData" or "body" 198 | Type string `json:"type,omitempty"` 199 | Format string `json:"format,omitempty"` 200 | Items *ParamItemObj `json:"items,omitempty"` // Required if type is "array" 201 | Schema *SchemaObj `json:"schema,omitempty"` // Required if type is "body" 202 | CollectionFormat string `json:"collectionFormat,omitempty"` // "multi" - this is valid only for parameters in "query" or "formData" 203 | Description string `json:"description,omitempty"` 204 | Default interface{} `json:"default,omitempty"` 205 | Required bool `json:"required,omitempty"` 206 | Enum 207 | additionalData 208 | } 209 | 210 | type _ParamObj ParamObj 211 | 212 | // MarshalJSON marshal OperationObj with additionalData inlined 213 | func (o ParamObj) MarshalJSON() ([]byte, error) { 214 | return o.marshalJSONWithStruct(_ParamObj(o)) 215 | } 216 | 217 | // ParamItemObj describes an property object, in param object or property of definition 218 | // see http://swagger.io/specification/#itemsObject 219 | type ParamItemObj struct { 220 | Ref string `json:"$ref,omitempty"` 221 | Type string `json:"type"` 222 | Format string `json:"format,omitempty"` 223 | Items *ParamItemObj `json:"items,omitempty"` // Required if type is "array" 224 | CollectionFormat string `json:"collectionFormat,omitempty"` // "multi" - this is valid only for parameters in "query" or "formData" 225 | } 226 | 227 | // Responses list of response object 228 | type Responses map[string]ResponseObj 229 | 230 | // ResponseObj describes a single response from an API Operation 231 | type ResponseObj struct { 232 | Ref string `json:"$ref,omitempty"` 233 | Description string `json:"description,omitempty"` 234 | Schema *SchemaObj `json:"schema,omitempty"` 235 | Headers interface{} `json:"headers,omitempty"` 236 | Examples interface{} `json:"examples,omitempty"` 237 | } 238 | 239 | // SchemaObj describes a schema for json format 240 | type SchemaObj struct { 241 | Ref string `json:"$ref,omitempty"` 242 | Description string `json:"description,omitempty"` 243 | Default interface{} `json:"default,omitempty"` 244 | Type string `json:"type,omitempty"` 245 | Format string `json:"format,omitempty"` 246 | Title string `json:"title,omitempty"` 247 | Items *SchemaObj `json:"items,omitempty"` // if type is array 248 | AdditionalProperties *SchemaObj `json:"additionalProperties,omitempty"` // if type is object (map[]) 249 | Properties map[string]SchemaObj `json:"properties,omitempty"` // if type is object 250 | TypeName string `json:"-"` // for internal using, passing typeName 251 | GoType string `json:"x-go-type,omitempty"` 252 | GoPropertyNames map[string]string `json:"x-go-property-names,omitempty"` 253 | GoPropertyTypes map[string]string `json:"x-go-property-types,omitempty"` 254 | } 255 | 256 | // NewSchemaObj Constructor function for SchemaObj struct type 257 | func NewSchemaObj(jsonType, typeName string) (so *SchemaObj) { 258 | so = &SchemaObj{ 259 | Type: jsonType, 260 | TypeName: typeName, 261 | } 262 | if typeName != "" { 263 | so.Ref = refDefinitionPrefix + typeName 264 | } 265 | return 266 | } 267 | 268 | // Checks whether current SchemaObj is "empty". A schema object is considered "empty" if it is an object without visible 269 | // (exported) properties, an array without elements, or in other cases when it has neither regular nor additional 270 | // properties, and format is not specified. Schema objects that describe common types ("string", "integer", "boolean" etc.) 271 | // are always considered non-empty. Same is true for "schema reference objects" (objects that have a non-empty Ref field). 272 | func (so *SchemaObj) isEmpty() bool { 273 | if isCommonName(so.TypeName) || so.Ref != "" { 274 | return false 275 | } 276 | 277 | switch so.Type { 278 | case "object": 279 | return len(so.Properties) == 0 280 | case "array": 281 | return so.Items == nil 282 | default: 283 | return len(so.Properties) == 0 && so.AdditionalProperties == nil && so.Format == "" 284 | } 285 | } 286 | 287 | // Export returns a "schema reference object" corresponding to this schema object. A "schema reference object" is an abridged 288 | // version of the original SchemaObj, having only two non-empty fields: Ref and TypeName. "Schema reference objects" 289 | // are used to refer original schema objects from other schemas. 290 | func (so SchemaObj) Export() SchemaObj { 291 | return SchemaObj{ 292 | Ref: so.Ref, 293 | TypeName: so.TypeName, 294 | } 295 | } 296 | 297 | type additionalData struct { 298 | data map[string]interface{} 299 | } 300 | 301 | // AddExtendedField add field to additional data map 302 | func (ad *additionalData) AddExtendedField(name string, value interface{}) { 303 | if ad.data == nil { 304 | ad.data = make(map[string]interface{}) 305 | } 306 | 307 | ad.data[name] = value 308 | } 309 | 310 | func (ad additionalData) marshalJSONWithStruct(i interface{}) ([]byte, error) { 311 | result, err := json.Marshal(i) 312 | if err != nil { 313 | return result, err 314 | } 315 | 316 | if len(ad.data) == 0 { 317 | return result, nil 318 | } 319 | 320 | dataJSON, err := json.Marshal(ad.data) 321 | if err != nil { 322 | return dataJSON, err 323 | } 324 | 325 | if string(result) == "{}" { 326 | return dataJSON, nil 327 | } 328 | 329 | result = append(result[:len(result)-1], ',') 330 | result = append(result, dataJSON[1:]...) 331 | 332 | return result, nil 333 | } 334 | -------------------------------------------------------------------------------- /entities_test.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import "testing" 4 | 5 | func TestPathItemHasMethod(t *testing.T) { 6 | item := PathItem{} 7 | item.Get = &OperationObj{} 8 | 9 | assertTrue(item.HasMethod("GET"), t) 10 | assertFalse(item.HasMethod("POST"), t) 11 | assertFalse(item.HasMethod("PUT"), t) 12 | assertFalse(item.HasMethod("HEAD"), t) 13 | assertFalse(item.HasMethod("DELETE"), t) 14 | assertFalse(item.HasMethod("OPTIONS"), t) 15 | assertFalse(item.HasMethod("PATCH"), t) 16 | assertFalse(item.HasMethod(""), t) 17 | } 18 | 19 | func TestAdditionalDataJSONMarshal(t *testing.T) { 20 | // empty object 21 | obj := additionalData{} 22 | _, err := obj.marshalJSONWithStruct(nil) 23 | assertTrue(err == nil, t) 24 | 25 | obj.AddExtendedField("x-custom-field", 1) 26 | data, err := obj.marshalJSONWithStruct(struct{}{}) 27 | assertTrue(err == nil, t) 28 | assertTrue(string(data) == `{"x-custom-field":1}`, t) 29 | } 30 | 31 | func assertTrue(v bool, t *testing.T) { 32 | if v != true { 33 | t.Fatal("value must return true") 34 | } 35 | } 36 | 37 | func assertFalse(v bool, t *testing.T) { 38 | if v != false { 39 | t.Fatal("value must return false") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package swgen_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/lazada/swgen" 7 | ) 8 | 9 | func ExampleGenerator_GenDocument() { 10 | // PetsRequest defines all params for /pets request 11 | type PetsRequest struct { 12 | Tags []string `schema:"tags" in:"query" required:"-" description:"tags to filter by"` 13 | Limit int32 `schema:"limit" in:"query" required:"-" description:"maximum number of results to return"` 14 | } 15 | 16 | // Pet contains information of a pet 17 | type Pet struct { 18 | ID int64 `json:"id"` 19 | Name string `json:"name"` 20 | Tag string `json:"tag"` 21 | } 22 | 23 | gen := swgen.NewGenerator() 24 | gen.SetHost("petstore.swagger.io").SetBasePath("/api") 25 | gen.SetInfo("Swagger Petstore (Simple)", "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", "http://helloreverb.com/terms/", "2.0") 26 | gen.SetLicense("MIT", "http://opensource.org/licenses/MIT") 27 | gen.SetContact("Swagger API team", "http://swagger.io", "foo@example.com") 28 | gen.AddSecurityDefinition("BasicAuth", swgen.SecurityDef{Type: swgen.SecurityBasicAuth}) 29 | 30 | pathInf := swgen.PathItemInfo{ 31 | Path: "/pets", 32 | Method: "GET", 33 | Title: "findPets", 34 | Description: "Returns all pets from the system that the user has access to", 35 | Tag: "v1", 36 | Deprecated: false, 37 | Security: []string{"BasicAuth"}, 38 | } 39 | pathInf.AddExtendedField("x-example", "example") 40 | 41 | gen.SetPathItem( 42 | pathInf, 43 | PetsRequest{}, // request object 44 | nil, // body data if any 45 | []Pet{}, // response object 46 | ) 47 | 48 | // extended field 49 | gen.AddExtendedField("x-uppercase-version", true) 50 | 51 | docData, _ := gen.GenDocument() 52 | fmt.Println(string(docData)) 53 | 54 | // output: 55 | // {"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"2.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http","https"],"paths":{"/pets":{"get":{"tags":["v1"],"summary":"findPets","description":"Returns all pets from the system that the user has access to","parameters":[{"name":"tags","in":"query","type":"array","items":{"type":"string"},"collectionFormat":"multi","description":"tags to filter by"},{"name":"limit","in":"query","type":"integer","format":"int32","description":"maximum number of results to return"}],"responses":{"200":{"description":"request success","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}}},"security":[{"BasicAuth":[]}],"x-example":"example"}}},"definitions":{"Pet":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"securityDefinitions":{"BasicAuth":{"type":"basic"}},"x-uppercase-version":true} 56 | } 57 | 58 | func ExampleGenerator_GenDocument_jsonrpc() { 59 | const ( 60 | // XServiceType is a swagger vendor extension 61 | XServiceType = `x-service-type` 62 | // XAttachVersionToHead is a swagger vendor extension 63 | XAttachVersionToHead = `x-attach-version-to-head` 64 | ) 65 | 66 | // Pet contains information of a pet 67 | type Pet struct { 68 | ID int64 `json:"id"` 69 | Name string `json:"name"` 70 | Tag string `json:"tag"` 71 | } 72 | 73 | gen := swgen.NewGenerator() 74 | gen.SetHost("petstore.swagger.io") 75 | gen.SetBasePath("/rpc") // set JSON-RPC path 76 | gen.SetInfo("Swagger Petstore (Simple)", "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", "http://helloreverb.com/terms/", "2.0") 77 | gen.SetLicense("MIT", "http://opensource.org/licenses/MIT") 78 | gen.SetContact("Swagger API team", "http://swagger.io", "foo@example.com") 79 | 80 | // set service type is JSON-RPC 81 | gen.AddExtendedField(XServiceType, swgen.ServiceTypeJSONRPC) 82 | gen.AddExtendedField(XAttachVersionToHead, false) 83 | 84 | pathInf := swgen.PathItemInfo{ 85 | Path: "addPet", // in JSON-RPC, use name of method for Path 86 | Method: "POST", 87 | Title: "Add new Pet", 88 | Description: "Add a new pet to the store", 89 | Tag: "v1", 90 | Deprecated: false, 91 | } 92 | 93 | gen.SetPathItem( 94 | pathInf, 95 | nil, // request object 96 | Pet{}, // body data if any 97 | Pet{}, // response object 98 | ) 99 | 100 | docData, _ := gen.GenDocument() 101 | fmt.Println(string(docData)) 102 | 103 | // output: 104 | // {"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"2.0"},"host":"petstore.swagger.io","basePath":"/rpc","schemes":["http","https"],"paths":{"addPet":{"post":{"tags":["v1"],"summary":"Add new Pet","description":"Add a new pet to the store","parameters":[{"name":"body","in":"body","schema":{"$ref":"#/definitions/Pet"},"required":true}],"responses":{"200":{"description":"request success","schema":{"$ref":"#/definitions/Pet"}}}}}},"definitions":{"Pet":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"x-attach-version-to-head":false,"x-service-type":"json-rpc"} 105 | } 106 | -------------------------------------------------------------------------------- /generator.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "reflect" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | // Generator create swagger document 12 | type Generator struct { 13 | doc Document 14 | host string // address of api in host:port format 15 | 16 | corsMu sync.RWMutex // mutex for CORS public API 17 | corsEnabled bool // allow cross-origin HTTP request 18 | corsAllowHeaders []string 19 | 20 | definitionAdded map[string]bool // index of TypeNames 21 | definitions defMap // list of all definition objects 22 | defQueue map[reflect.Type]struct{} // queue of reflect.Type objects waiting for analysis 23 | paths map[string]PathItem // list all of paths object 24 | typesMap map[reflect.Type]interface{} 25 | 26 | indentJSON bool 27 | reflectGoTypes bool 28 | 29 | mu sync.Mutex // mutex for Generator's public API 30 | } 31 | 32 | type defMap map[reflect.Type]SchemaObj 33 | 34 | func (m *defMap) GenDefinitions() (result map[string]SchemaObj) { 35 | if m == nil { 36 | return nil 37 | } 38 | 39 | result = make(map[string]SchemaObj) 40 | for t, typeDef := range *m { 41 | typeDef.Ref = "" // first (top) level Swagger definitions are never references 42 | if _, ok := result[typeDef.TypeName]; ok { 43 | typeName := goType(t) 44 | result[typeName] = typeDef 45 | } else { 46 | result[typeDef.TypeName] = typeDef 47 | } 48 | } 49 | return 50 | } 51 | 52 | // NewGenerator create a new Generator 53 | func NewGenerator() *Generator { 54 | g := &Generator{} 55 | 56 | g.definitions = make(map[reflect.Type]SchemaObj) 57 | g.definitionAdded = make(map[string]bool) 58 | 59 | g.defQueue = make(map[reflect.Type]struct{}) 60 | g.paths = make(map[string]PathItem) // list all of paths object 61 | g.typesMap = make(map[reflect.Type]interface{}) 62 | 63 | g.doc.Schemes = []string{"http", "https"} 64 | g.doc.Paths = make(map[string]PathItem) 65 | g.doc.Definitions = make(map[string]SchemaObj) 66 | g.doc.SecurityDefinitions = make(map[string]SecurityDef) 67 | g.doc.Version = "2.0" 68 | g.doc.BasePath = "/" 69 | 70 | // set default Access-Control-Allow-Headers of swagger.json 71 | g.corsAllowHeaders = []string{"Content-Type", "api_key", "Authorization"} 72 | 73 | return g 74 | } 75 | 76 | // IndentJSON controls JSON indentation 77 | func (g *Generator) IndentJSON(enabled bool) *Generator { 78 | g.mu.Lock() 79 | g.indentJSON = enabled 80 | g.mu.Unlock() 81 | return g 82 | } 83 | 84 | // ReflectGoTypes controls JSON indentation 85 | func (g *Generator) ReflectGoTypes(enabled bool) *Generator { 86 | g.mu.Lock() 87 | g.reflectGoTypes = enabled 88 | g.mu.Unlock() 89 | return g 90 | } 91 | 92 | // EnableCORS enable HTTP handler support CORS 93 | func (g *Generator) EnableCORS(b bool, allowHeaders ...string) *Generator { 94 | g.corsMu.Lock() 95 | g.corsEnabled = b 96 | if len(allowHeaders) != 0 { 97 | g.corsAllowHeaders = append(g.corsAllowHeaders, allowHeaders...) 98 | } 99 | g.corsMu.Unlock() 100 | return g 101 | } 102 | 103 | func (g *Generator) writeCORSHeaders(w http.ResponseWriter) { 104 | g.corsMu.RLock() 105 | defer g.corsMu.RUnlock() 106 | 107 | if !g.corsEnabled { 108 | return 109 | } 110 | 111 | w.Header().Set("Access-Control-Allow-Origin", "*") 112 | w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, OPTIONS") 113 | w.Header().Set("Access-Control-Allow-Headers", strings.Join(g.corsAllowHeaders, ", ")) 114 | } 115 | 116 | // SetHost set host info for swagger specification 117 | func (g *Generator) SetHost(host string) *Generator { 118 | g.mu.Lock() 119 | g.host = host 120 | g.mu.Unlock() 121 | return g 122 | } 123 | 124 | // SetBasePath set host info for swagger specification 125 | func (g *Generator) SetBasePath(basePath string) *Generator { 126 | basePath = "/" + strings.Trim(basePath, "/") 127 | g.mu.Lock() 128 | g.doc.BasePath = basePath 129 | g.mu.Unlock() 130 | return g 131 | } 132 | 133 | // SetContact set contact information for API 134 | func (g *Generator) SetContact(name, url, email string) *Generator { 135 | ct := ContactObj{ 136 | Name: name, 137 | URL: url, 138 | Email: email, 139 | } 140 | 141 | g.mu.Lock() 142 | g.doc.Info.Contact = ct 143 | g.mu.Unlock() 144 | return g 145 | } 146 | 147 | // SetInfo set information about API 148 | func (g *Generator) SetInfo(title, description, term, version string) *Generator { 149 | info := InfoObj{ 150 | Title: title, 151 | Description: description, 152 | TermsOfService: term, 153 | Version: version, 154 | } 155 | 156 | g.mu.Lock() 157 | g.doc.Info = info 158 | g.mu.Unlock() 159 | return g 160 | } 161 | 162 | // SetLicense set license information for API 163 | func (g *Generator) SetLicense(name, url string) *Generator { 164 | ls := LicenseObj{ 165 | Name: name, 166 | URL: url, 167 | } 168 | 169 | g.mu.Lock() 170 | g.doc.Info.License = ls 171 | g.mu.Unlock() 172 | return g 173 | } 174 | 175 | // AddExtendedField add vendor extension field to document 176 | func (g *Generator) AddExtendedField(name string, value interface{}) *Generator { 177 | g.mu.Lock() 178 | g.doc.AddExtendedField(name, value) 179 | g.mu.Unlock() 180 | return g 181 | } 182 | 183 | // AddSecurityDefinition adds shared security definition to document 184 | func (g *Generator) AddSecurityDefinition(name string, def SecurityDef) *Generator { 185 | g.mu.Lock() 186 | g.doc.SecurityDefinitions[name] = def 187 | g.mu.Unlock() 188 | return g 189 | } 190 | 191 | // AddTypeMap add rule to use dst interface instead of src 192 | func (g *Generator) AddTypeMap(src interface{}, dst interface{}) *Generator { 193 | g.mu.Lock() 194 | g.typesMap[reflect.TypeOf(src)] = dst 195 | g.mu.Unlock() 196 | return g 197 | } 198 | 199 | func (g *Generator) getMappedType(t reflect.Type) (dst interface{}, found bool) { 200 | dst, found = g.typesMap[t] 201 | return 202 | } 203 | 204 | func (g *Generator) isJSONRPC() bool { 205 | serviceType, found := g.doc.data["x-service-type"] 206 | if !found { 207 | return false 208 | } 209 | 210 | t, isServiceType := serviceType.(ServiceType) 211 | return isServiceType && t == ServiceTypeJSONRPC 212 | } 213 | 214 | // genDocument returns document specification in JSON string (in []byte) 215 | func (g *Generator) genDocument(host *string) ([]byte, error) { 216 | g.mu.Lock() 217 | defer g.mu.Unlock() 218 | 219 | // ensure that all definition in queue is parsed before generating 220 | g.parseDefInQueue() 221 | g.doc.Definitions = g.definitions.GenDefinitions() 222 | if g.host != "" || host == nil { 223 | g.doc.Host = g.host 224 | } else { 225 | g.doc.Host = *host 226 | } 227 | g.doc.Paths = make(map[string]PathItem) 228 | 229 | isJSONRPC := g.isJSONRPC() 230 | for path, item := range g.paths { 231 | if isJSONRPC { 232 | if !item.HasMethod("POST") { 233 | continue 234 | } 235 | 236 | item.Get = nil 237 | item.Put = nil 238 | item.Delete = nil 239 | item.Options = nil 240 | item.Head = nil 241 | item.Patch = nil 242 | } 243 | g.doc.Paths[path] = item 244 | } 245 | 246 | var ( 247 | data []byte 248 | err error 249 | ) 250 | if g.indentJSON { 251 | data, err = json.MarshalIndent(g.doc, "", " ") 252 | } else { 253 | data, err = json.Marshal(g.doc) 254 | } 255 | 256 | return data, err 257 | } 258 | 259 | // GenDocument returns document specification in JSON string (in []byte) 260 | func (g *Generator) GenDocument() ([]byte, error) { 261 | // pass nil here to set host as g.host 262 | return g.genDocument(nil) 263 | } 264 | 265 | // ServeHTTP implements http.Handler to server swagger.json document 266 | func (g *Generator) ServeHTTP(w http.ResponseWriter, r *http.Request) { 267 | data, err := g.genDocument(&r.URL.Host) 268 | if err != nil { 269 | http.Error(w, err.Error(), http.StatusInternalServerError) 270 | } 271 | 272 | w.Header().Set("Content-Type", "application/json") 273 | 274 | g.writeCORSHeaders(w) 275 | 276 | w.Write(data) 277 | } 278 | -------------------------------------------------------------------------------- /generator_singleton.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import "net/http" 4 | 5 | // singleton package generator 6 | var gen = NewGenerator() 7 | 8 | // EnableCORS enable HTTP handler support CORS 9 | func EnableCORS(b bool, allowHeaders ...string) *Generator { 10 | return gen.EnableCORS(b, allowHeaders...) 11 | } 12 | 13 | // SetHost set host info for swagger specification 14 | func SetHost(host string) *Generator { 15 | return gen.SetHost(host) 16 | } 17 | 18 | // SetBasePath set host info for swagger specification 19 | func SetBasePath(basePath string) *Generator { 20 | return gen.SetBasePath(basePath) 21 | } 22 | 23 | // SetContact set contact information for API 24 | func SetContact(name, url, email string) *Generator { 25 | return gen.SetContact(name, url, email) 26 | } 27 | 28 | // SetInfo set information about API 29 | func SetInfo(title, description, term, version string) *Generator { 30 | return gen.SetInfo(title, description, term, version) 31 | } 32 | 33 | // SetLicense set license information for API 34 | func SetLicense(name, url string) *Generator { 35 | return gen.SetLicense(name, url) 36 | } 37 | 38 | // AddExtendedField add vendor extension field to document 39 | func AddExtendedField(name string, value interface{}) *Generator { 40 | return gen.AddExtendedField(name, value) 41 | } 42 | 43 | // AddTypeMap add rule to use dst interface instead of src 44 | func AddTypeMap(src interface{}, dst interface{}) *Generator { 45 | return gen.AddTypeMap(src, dst) 46 | } 47 | 48 | // GenDocument returns document specification in JSON string (in []byte) 49 | func GenDocument() ([]byte, error) { 50 | return gen.GenDocument() 51 | } 52 | 53 | // ServeHTTP implements http.HandleFunc to server swagger.json document 54 | func ServeHTTP(w http.ResponseWriter, r *http.Request) { 55 | gen.ServeHTTP(w, r) 56 | } 57 | -------------------------------------------------------------------------------- /generator_test.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "net/http" 7 | "net/http/httptest" 8 | "os" 9 | "path" 10 | "reflect" 11 | "strings" 12 | "testing" 13 | "time" 14 | 15 | "github.com/kr/pretty" 16 | "github.com/lazada/swgen/sample" 17 | ) 18 | 19 | type TestSampleStruct struct { 20 | SimpleString string `json:"simple_string"` 21 | SimpleInt int `json:"simple_int"` 22 | } 23 | 24 | type testEmptyStruct struct{} 25 | 26 | type testSimpleStruct struct { 27 | SimpleString string `json:"simple_string" schema:"simple_string"` 28 | SimpleInt int `json:"simple_int" schema:"simple_int"` 29 | SimpleInt32 int32 `json:"simple_int32" schema:"simple_int32"` 30 | SimpleInt64 int64 `json:"simple_int64" schema:"simple_int64"` 31 | SimpleUInt32 uint32 `json:"simple_uint32" schema:"simple_uint32"` 32 | SimpleUInt64 uint64 `json:"simple_uint64" schema:"simple_uint64"` 33 | SimpleFloat32 float32 `json:"simple_float32" schema:"simple_float32"` 34 | SimpleFloat64 float64 `json:"simple_float64" schema:"simple_float64"` 35 | SimpleBool bool `json:"simple_bool" schema:"simple_bool"` 36 | IgnoreField string `json:"-" schema:"-"` 37 | } 38 | 39 | type testSimpleSlices struct { 40 | ListString []string `json:"list_string"` 41 | ListInt []int `json:"list_int"` 42 | ListInt32 []int32 `json:"list_int32"` 43 | ListInt64 []int64 `json:"list_int64"` 44 | ListUInt32 []uint32 `json:"list_uint32"` 45 | ListUInt64 []uint64 `json:"list_uint64"` 46 | ListFloat32 []float32 `json:"list_float32"` 47 | ListFloat64 []float64 `json:"list_float64"` 48 | ListBool []bool `json:"list_bool"` 49 | } 50 | 51 | type testSimpleMaps struct { 52 | MapString map[string]string `json:"map_string"` 53 | MapInt map[string]int `json:"map_int"` 54 | MapInt32 map[string]int32 `json:"map_int32"` 55 | MapInt64 map[string]int64 `json:"map_int64"` 56 | MapUInt32 map[string]uint32 `json:"map_uint32"` 57 | MapUInt64 map[string]uint64 `json:"map_uint64"` 58 | MapFloat32 map[string]float32 `json:"map_float32"` 59 | MapFloat64 map[string]float64 `json:"map_float64"` 60 | MapBool map[string]bool `json:"map_bool"` 61 | } 62 | 63 | type testSimpleMapList struct { 64 | MapListString []map[string]string `json:"map_list_string"` 65 | MapListInt []map[string]int `json:"map_list_int"` 66 | MapListInt32 []map[string]int32 `json:"map_list_int32"` 67 | MapListInt64 []map[string]int64 `json:"map_list_int64"` 68 | MapListUInt32 []map[string]uint32 `json:"map_list_uint32"` 69 | MapListUInt64 []map[string]uint64 `json:"map_list_uint64"` 70 | MapListFloat32 []map[string]float32 `json:"map_list_float32"` 71 | MapListFloat64 []map[string]float64 `json:"map_list_float64"` 72 | MapListBool []map[string]bool `json:"map_list_bool"` 73 | } 74 | 75 | type testSubTypes struct { 76 | TestSimpleStruct testSimpleStruct `json:"test_simple_struct"` 77 | TestSimpleSlices testSimpleSlices `json:"test_simple_slices"` 78 | TestSimpleMaps testSimpleMaps `json:"test_simple_maps"` 79 | TestSimpleMapList testSimpleMapList `json:"test_simple_map_list"` 80 | } 81 | 82 | type testPathParam struct { 83 | ID uint64 `json:"id" path:"id" required:"-"` 84 | Cat string `json:"category" path:"category"` 85 | } 86 | 87 | type simpleTestReplacement struct { 88 | ID uint64 `json:"id"` 89 | Cat string `json:"category"` 90 | } 91 | 92 | type deepReplacementTag struct { 93 | TestField1 string `json:"test_field_1" swgen_type:"double"` 94 | } 95 | 96 | type testWrapParams struct { 97 | SimpleTestReplacement simpleTestReplacement `json:"simple_test_replacement"` 98 | ReplaceByTag int `json:"should_be_sting" swgen_type:"string"` 99 | DeepReplacementTag deepReplacementTag `json:"deep_replacement"` 100 | } 101 | 102 | type simpleDateTime struct { 103 | Time time.Time `json:"time"` 104 | } 105 | 106 | type sliceDateTime struct { 107 | Items []simpleDateTime `json:"items"` 108 | } 109 | 110 | type mapDateTime struct { 111 | Items map[string]simpleDateTime `json:"items"` 112 | } 113 | 114 | type paramStructMap struct { 115 | Field1 int `schema:"field1"` 116 | Field2 string `schema:"field2"` 117 | Field3 simpleTestReplacement `schema:"field3"` 118 | } 119 | 120 | type AnonymousField struct { 121 | AnonProp int `json:"anonProp"` 122 | } 123 | 124 | type mixedStruct struct { 125 | AnonymousField 126 | FieldQuery int `schema:"fieldQuery"` 127 | FieldBody int `json:"fieldBody"` 128 | } 129 | 130 | type typeMapHolder struct { 131 | M typeMap `json:"m"` 132 | } 133 | 134 | type typeMap struct { 135 | R1 int `json:"1"` 136 | R2 int `json:"2"` 137 | R3 int `json:"3"` 138 | R4 int `json:"4"` 139 | R5 int `json:"5"` 140 | } 141 | 142 | type Gender int 143 | 144 | func (Gender) GetEnumSlices() ([]interface{}, []string) { 145 | return []interface{}{ 146 | PreferNotToDisclose, 147 | Male, 148 | Female, 149 | LGBT, 150 | }, []string{ 151 | "PreferNotToDisclose", 152 | "Male", 153 | "Female", 154 | "LGBT", 155 | } 156 | } 157 | 158 | const ( 159 | PreferNotToDisclose Gender = iota 160 | Male 161 | Female 162 | LGBT 163 | ) 164 | 165 | type Flag string 166 | 167 | func (Flag) GetEnumSlices() ([]interface{}, []string) { 168 | return []interface{}{Flag("Foo"), Flag("Bar")}, []string{"Foo", "Bar"} 169 | } 170 | 171 | type mixedStructWithEnumer struct { 172 | Gender Gender `schema:"gender"` 173 | Flag Flag `schema:"flag"` 174 | } 175 | 176 | type sliceType []testSimpleStruct 177 | 178 | type NullFloat64 struct{} 179 | 180 | func (NullFloat64) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 181 | typeName = "NullFloat64" 182 | typeDef = SchemaFromCommonName(CommonNameFloat) 183 | return 184 | } 185 | 186 | type NullBool struct{} 187 | 188 | func (NullBool) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 189 | typeName = "NullBool" 190 | typeDef = SchemaFromCommonName(CommonNameBoolean) 191 | return 192 | } 193 | 194 | type NullString struct{} 195 | 196 | func (NullString) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 197 | typeName = "NullString" 198 | typeDef = SchemaFromCommonName(CommonNameString) 199 | return 200 | } 201 | 202 | type NullInt64 struct{} 203 | 204 | func (NullInt64) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 205 | typeName = "NullInt64" 206 | typeDef = SchemaFromCommonName(CommonNameLong) 207 | return 208 | } 209 | 210 | type NullDateTime struct{} 211 | 212 | func (NullDateTime) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 213 | typeName = "NullDateTime" 214 | typeDef = SchemaFromCommonName(CommonNameDateTime) 215 | return 216 | } 217 | 218 | type NullDate struct{} 219 | 220 | func (NullDate) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 221 | typeName = "NullDate" 222 | typeDef = SchemaFromCommonName(CommonNameDate) 223 | return 224 | } 225 | 226 | type NullTimestamp struct{} 227 | 228 | func (NullTimestamp) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 229 | typeName = "NullTimestamp" 230 | typeDef = SchemaFromCommonName(CommonNameLong) 231 | return 232 | } 233 | 234 | type testDefaults struct { 235 | Field1 int `json:"field1" default:"25"` 236 | Field2 float64 `json:"field2" default:"25.5"` 237 | Field3 string `json:"field3" default:"test"` 238 | Field4 bool `json:"field4" default:"true"` 239 | Field5 []int `json:"field5" default:"[1, 2, 3]"` 240 | Field6 map[string]int `json:"field6" default:"{\"test\": 1}"` 241 | Field7 *uint `json:"field7" default:"25"` 242 | } 243 | 244 | type NullTypes struct { 245 | Float NullFloat64 `json:"null_float"` 246 | Bool NullBool `json:"null_bool"` 247 | String NullString `json:"null_string"` 248 | Int NullInt64 `json:"null_int"` 249 | DateTime NullDateTime `json:"null_date_time"` 250 | Date NullDate `json:"null_date"` 251 | Timestamp NullTimestamp `json:"null_timestamp"` 252 | } 253 | 254 | type Unknown struct { 255 | Anything interface{} `json:"anything"` 256 | Whatever *json.RawMessage `json:"whatever"` 257 | } 258 | 259 | var _ IDefinition = definitionExample{} 260 | 261 | type definitionExample struct{} 262 | 263 | func (defEx definitionExample) SwgenDefinition() (typeName string, typeDef SchemaObj, err error) { 264 | return "", SchemaObj{ 265 | Type: "string", 266 | Format: "byte", 267 | }, nil 268 | } 269 | 270 | func createPathItemInfo(path, method, title, description, tag string, deprecated bool) PathItemInfo { 271 | return PathItemInfo{ 272 | Path: path, 273 | Method: method, 274 | Title: title, 275 | Description: description, 276 | Tag: tag, 277 | Deprecated: deprecated, 278 | } 279 | } 280 | 281 | func TestREST(t *testing.T) { 282 | gen := NewGenerator() 283 | gen.SetHost("localhost"). 284 | SetBasePath("/"). 285 | SetInfo("swgen title", "swgen description", "term", "2.0"). 286 | SetLicense("BEER-WARE", "https://fedoraproject.org/wiki/Licensing/Beerware"). 287 | SetContact("Dylan Noblitt", "http://example.com", "dylan.noblitt@example.com"). 288 | AddExtendedField("x-service-type", ServiceTypeRest). 289 | ReflectGoTypes(true). 290 | IndentJSON(true) 291 | 292 | gen.AddTypeMap(simpleTestReplacement{}, "") 293 | gen.AddTypeMap(sliceType{}, float64(0)) 294 | gen.AddTypeMap(typeMap{}, map[string]int{}) 295 | 296 | var emptyInterface interface{} 297 | 298 | gen.SetPathItem(createPathItemInfo("/V1/test1", "GET", "test1 name", "test1 description", "v1", false), emptyInterface, emptyInterface, testSimpleStruct{}) 299 | gen.SetPathItem(createPathItemInfo("/V1/test2", "GET", "test2 name", "test2 description", "v1", false), testSimpleStruct{}, emptyInterface, testSimpleSlices{}) 300 | gen.SetPathItem(createPathItemInfo("/V1/test3", "PUT", "test3 name", "test3 description", "v1", false), emptyInterface, testSimpleSlices{}, testSimpleMaps{}) 301 | gen.SetPathItem(createPathItemInfo("/V1/test4", "POST", "test4 name", "test4 description", "v1", false), emptyInterface, testSimpleMaps{}, testSimpleMapList{}) 302 | gen.SetPathItem(createPathItemInfo("/V1/test5", "DELETE", "test5 name", "test5 description", "v1", false), emptyInterface, testSimpleMapList{}, testSubTypes{}) 303 | gen.SetPathItem(createPathItemInfo("/V1/test6", "PATCH", "test6 name", "test6 description", "v1", false), emptyInterface, testSubTypes{}, testSimpleStruct{}) 304 | gen.SetPathItem(createPathItemInfo("/V1/test7", "OPTIONS", "test7 name", "test7 description", "v1", false), emptyInterface, emptyInterface, testSimpleSlices{}) 305 | gen.SetPathItem(createPathItemInfo("/V1/test8", "GET", "test8v1 name", "test8v1 description", "v1", false), paramStructMap{}, emptyInterface, map[string]testSimpleStruct{}) 306 | gen.SetPathItem(createPathItemInfo("/V1/test9", "GET", "test9 name", "test9 description", "v1", false), mixedStruct{}, mixedStruct{}, map[string]testSimpleStruct{}) 307 | 308 | gen.SetPathItem(createPathItemInfo("/V1/combine", "GET", "test1 name", "test1 description", "v1", true), emptyInterface, emptyInterface, testSimpleStruct{}) 309 | gen.SetPathItem(createPathItemInfo("/V1/combine", "PUT", "test3 name", "test3 description", "v1", true), emptyInterface, testSimpleSlices{}, testSimpleMaps{}) 310 | gen.SetPathItem(createPathItemInfo("/V1/combine", "POST", "test4 name", "test4 description", "v1", true), emptyInterface, testSimpleMaps{}, testSimpleMapList{}) 311 | gen.SetPathItem(createPathItemInfo("/V1/combine", "DELETE", "test5 name", "test5 description", "v1", true), emptyInterface, testSimpleMapList{}, testSubTypes{}) 312 | gen.SetPathItem(createPathItemInfo("/V1/combine", "PATCH", "test6 name", "test6 description", "v1", true), emptyInterface, testSubTypes{}, testSimpleStruct{}) 313 | gen.SetPathItem(createPathItemInfo("/V1/combine", "OPTIONS", "test7 name", "test7 description", "v1", true), emptyInterface, testSubTypes{}, testSimpleStruct{}) 314 | 315 | gen.SetPathItem(createPathItemInfo("/V1/pathParams/{category:[a-zA-Z]{32}}/{id:[0-9]+}", "GET", "test8 name", "test8 description", "V1", false), testPathParam{}, emptyInterface, testSimpleStruct{}) 316 | 317 | //anonymous types: 318 | gen.SetPathItem(createPathItemInfo("/V1/anonymous1", "POST", "test10 name", "test10 description", "v1", false), emptyInterface, testSimpleStruct{}, map[string]int64{}) 319 | gen.SetPathItem(createPathItemInfo("/V1/anonymous2", "POST", "test11 name", "test11 description", "v1", false), emptyInterface, testSimpleStruct{}, map[float64]string{}) 320 | gen.SetPathItem(createPathItemInfo("/V1/anonymous3", "POST", "test12 name", "test12 description", "v1", false), emptyInterface, testSimpleStruct{}, []string{}) 321 | gen.SetPathItem(createPathItemInfo("/V1/anonymous4", "POST", "test13 name", "test13 description", "v1", false), emptyInterface, testSimpleStruct{}, []int{}) 322 | gen.SetPathItem(createPathItemInfo("/V1/anonymous5", "POST", "test14 name", "test14 description", "v1", false), emptyInterface, testSimpleStruct{}, "") 323 | gen.SetPathItem(createPathItemInfo("/V1/anonymous6", "POST", "test15 name", "test15 description", "v1", false), emptyInterface, testSimpleStruct{}, true) 324 | gen.SetPathItem(createPathItemInfo("/V1/anonymous7", "POST", "test16 name", "test16 description", "v1", false), emptyInterface, testSimpleStruct{}, map[string]testSimpleStruct{}) 325 | 326 | gen.SetPathItem(createPathItemInfo("/V1/typeReplacement1", "POST", "test9 name", "test9 description", "v1", false), emptyInterface, testSubTypes{}, testWrapParams{}) 327 | 328 | gen.SetPathItem(createPathItemInfo("/V1/date1", "POST", "test date 1 name", "test date 1 description", "v1", false), emptyInterface, testSimpleStruct{}, simpleDateTime{}) 329 | gen.SetPathItem(createPathItemInfo("/V1/date2", "POST", "test date 2 name", "test date 2 description", "v1", false), emptyInterface, testSimpleStruct{}, sliceDateTime{}) 330 | gen.SetPathItem(createPathItemInfo("/V1/date3", "POST", "test date 3 name", "test date 3 description", "v1", false), emptyInterface, testSimpleStruct{}, mapDateTime{}) 331 | gen.SetPathItem(createPathItemInfo("/V1/date4", "POST", "test date 4 name", "test date 4 description", "v1", false), emptyInterface, testSimpleStruct{}, []mapDateTime{}) 332 | 333 | gen.SetPathItem(createPathItemInfo("/V1/slice1", "POST", "test slice 1 name", "test slice 1 description", "v1", false), emptyInterface, testSimpleStruct{}, []mapDateTime{}) 334 | gen.SetPathItem(createPathItemInfo("/V1/slice2", "POST", "test slice 2 name", "test slice 2 description", "v1", false), emptyInterface, testSimpleStruct{}, sliceType{}) 335 | 336 | gen.SetPathItem(createPathItemInfo("/V1/IDefinition1", "POST", "test IDefinition1 name", "test IDefinition1 description", "v1", false), emptyInterface, definitionExample{}, definitionExample{}) 337 | gen.SetPathItem(createPathItemInfo("/V1/nullTypes", "GET", "test nulltypes", "test nulltypes", "v1", false), emptyInterface, NullTypes{}, NullTypes{}) 338 | 339 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes1", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, "", 10) 340 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes2", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, true, 1.1) 341 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes3", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, int64(10), "") 342 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes4", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, int64(10), "") 343 | 344 | gen.SetPathItem(createPathItemInfo("/V1/defaeults1", "GET", "default", "test defaults", "v1", false), emptyInterface, emptyInterface, testDefaults{}) 345 | gen.SetPathItem(createPathItemInfo("/V1/unknown", "POST", "test unknown types", "test unknown types", "v1", false), emptyInterface, Unknown{}, Unknown{}) 346 | 347 | gen.SetPathItem(createPathItemInfo("/V1/empty", "POST", "test empty struct", "test empty struct", "v1", false), testEmptyStruct{}, nil, testEmptyStruct{}) 348 | 349 | gen.SetPathItem(createPathItemInfo("/V1/struct-collision", "POST", "test struct name collision", "test struct name collision", "v1", false), nil, TestSampleStruct{}, TestSampleStruct{}) 350 | gen.SetPathItem(createPathItemInfo("/V2/struct-collision", "POST", "test struct name collision", "test struct name collision", "v2", false), nil, sample.TestSampleStruct{}, sample.TestSampleStruct{}) 351 | 352 | gen.SetPathItem(createPathItemInfo("/V1/type-map", "POST", "test type mapping", "test type mapping", "v1", false), nil, nil, typeMapHolder{}) 353 | 354 | bytes, err := gen.GenDocument() 355 | if err != nil { 356 | t.Fatalf("Failed to generate Swagger JSON document: %s", err.Error()) 357 | } 358 | 359 | if err := writeLastRun("test_REST_last_run.json", bytes); err != nil { 360 | t.Fatalf("Failed write last run data to a file: %s", err.Error()) 361 | } 362 | 363 | assertTrue(checkResult(bytes, "test_REST.json", t), t) 364 | } 365 | 366 | func TestJsonRpc(t *testing.T) { 367 | gen := NewGenerator() 368 | gen.SetHost("localhost") 369 | gen.SetInfo("swgen title", "swgen description", "term", "2.0") 370 | gen.SetLicense("BEER-WARE", "https://fedoraproject.org/wiki/Licensing/Beerware") 371 | gen.SetContact("Dylan Noblitt", "http://example.com", "dylan.noblitt@example.com") 372 | gen.AddExtendedField("x-service-type", ServiceTypeJSONRPC) 373 | gen.AddTypeMap(simpleTestReplacement{}, "") 374 | gen.AddTypeMap(sliceType{}, "") 375 | gen.IndentJSON(true) 376 | 377 | var emptyInterface interface{} 378 | 379 | gen.SetPathItem(createPathItemInfo("/V1/test1", "POST", "test1 name", "test1 description", "v1", true), emptyInterface, emptyInterface, testSimpleStruct{}) 380 | gen.SetPathItem(createPathItemInfo("/V1/test2", "POST", "test2 name", "test2 description", "v1", true), testSimpleStruct{}, emptyInterface, testSimpleSlices{}) 381 | gen.SetPathItem(createPathItemInfo("/V1/test3", "POST", "test3 name", "test3 description", "v1", true), emptyInterface, testSimpleSlices{}, testSimpleMaps{}) 382 | gen.SetPathItem(createPathItemInfo("/V1/test4", "POST", "test4 name", "test4 description", "v1", true), emptyInterface, testSimpleMaps{}, testSimpleMapList{}) 383 | gen.SetPathItem(createPathItemInfo("/V1/test5", "POST", "test5 name", "test5 description", "v1", true), emptyInterface, testSimpleMapList{}, testSubTypes{}) 384 | gen.SetPathItem(createPathItemInfo("/V1/test6", "POST", "test6 name", "test6 description", "v1", true), emptyInterface, testSubTypes{}, testSimpleStruct{}) 385 | gen.SetPathItem(createPathItemInfo("/V1/test7", "POST", "test7 name", "test7 description", "v1", true), emptyInterface, emptyInterface, testSimpleSlices{}) 386 | gen.SetPathItem(createPathItemInfo("/V1/test8", "POST", "test8v1 name", "test8v1 description", "v1", true), emptyInterface, paramStructMap{}, map[string]testSimpleStruct{}) 387 | gen.SetPathItem(createPathItemInfo("/V1/test9", "POST", "test9 name", "test9 description", "v1", true), mixedStruct{}, mixedStruct{}, map[string]testSimpleStruct{}) 388 | gen.SetPathItem(createPathItemInfo("/V1/test10", "POST", "test10 name", "test10 description", "v1", true), mixedStructWithEnumer{}, mixedStruct{}, map[string]testSimpleStruct{}) 389 | 390 | gen.SetPathItem(createPathItemInfo("/V1/typeReplacement1", "POST", "test9 name", "test9 description", "v1", false), emptyInterface, testSubTypes{}, testWrapParams{}) 391 | 392 | //anonymous types: 393 | gen.SetPathItem(createPathItemInfo("/V1/anonymous1", "POST", "test10 name", "test10 description", "v1", false), emptyInterface, emptyInterface, map[string]int64{}) 394 | gen.SetPathItem(createPathItemInfo("/V1/anonymous2", "POST", "test11 name", "test11 description", "v1", false), emptyInterface, emptyInterface, map[float64]string{}) 395 | gen.SetPathItem(createPathItemInfo("/V1/anonymous3", "POST", "test12 name", "test12 description", "v1", false), emptyInterface, emptyInterface, []string{}) 396 | gen.SetPathItem(createPathItemInfo("/V1/anonymous4", "POST", "test13 name", "test13 description", "v1", false), emptyInterface, emptyInterface, []int{}) 397 | gen.SetPathItem(createPathItemInfo("/V1/anonymous5", "POST", "test14 name", "test14 description", "v1", false), emptyInterface, emptyInterface, "") 398 | gen.SetPathItem(createPathItemInfo("/V1/anonymous6", "POST", "test15 name", "test15 description", "v1", false), emptyInterface, emptyInterface, true) 399 | gen.SetPathItem(createPathItemInfo("/V1/anonymous7", "POST", "test16 name", "test16 description", "v1", false), emptyInterface, emptyInterface, map[string]testSimpleStruct{}) 400 | 401 | gen.SetPathItem(createPathItemInfo("/V1/date1", "POST", "test date 1 name", "test date 1 description", "v1", false), emptyInterface, emptyInterface, simpleDateTime{}) 402 | gen.SetPathItem(createPathItemInfo("/V1/date2", "POST", "test date 2 name", "test date 2 description", "v1", false), emptyInterface, emptyInterface, sliceDateTime{}) 403 | gen.SetPathItem(createPathItemInfo("/V1/date3", "POST", "test date 3 name", "test date 3 description", "v1", false), emptyInterface, emptyInterface, mapDateTime{}) 404 | gen.SetPathItem(createPathItemInfo("/V1/date4", "POST", "test date 4 name", "test date 4 description", "v1", false), emptyInterface, emptyInterface, []mapDateTime{}) 405 | 406 | gen.SetPathItem(createPathItemInfo("/V1/slice1", "POST", "test slice 1 name", "test slice 1 description", "v1", false), emptyInterface, emptyInterface, []mapDateTime{}) 407 | gen.SetPathItem(createPathItemInfo("/V1/slice2", "POST", "test slice 2 name", "test slice 2 description", "v1", false), emptyInterface, emptyInterface, sliceType{}) 408 | 409 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes1", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, "", 10) 410 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes2", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, true, 1.1) 411 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes3", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, int64(10), "") 412 | gen.SetPathItem(createPathItemInfo("/V1/primitiveTypes4", "POST", "testPrimitives", "test Primitives", "v1", false), emptyInterface, int64(10), "") 413 | 414 | gen.SetPathItem(createPathItemInfo("/V1/defaults1", "POST", "default", "test defaults", "v1", false), emptyInterface, emptyInterface, testDefaults{}) 415 | 416 | bytes, err := gen.GenDocument() 417 | if err != nil { 418 | t.Fatalf("can not generate document: %s", err.Error()) 419 | } 420 | 421 | if err := writeLastRun("test_JSON-RPC_last_run.json", bytes); err != nil { 422 | t.Fatalf("Failed write last run data to a file: %s", err.Error()) 423 | } 424 | 425 | assertTrue(checkResult(bytes, "test_JSON-RPC.json", t), t) 426 | } 427 | 428 | func getTestDataDir(filename string) string { 429 | pwd, err := os.Getwd() 430 | if err != nil { 431 | return filename 432 | } 433 | 434 | return path.Join(pwd, "testdata", filename) 435 | } 436 | 437 | func writeLastRun(filename string, data []byte) error { 438 | return ioutil.WriteFile(getTestDataDir(filename), data, os.ModePerm) 439 | } 440 | 441 | func readTestFile(filename string) ([]byte, error) { 442 | bytes, readError := ioutil.ReadFile(getTestDataDir(filename)) 443 | if readError != nil { 444 | return []byte{}, readError 445 | } 446 | 447 | return bytes, nil 448 | } 449 | 450 | func checkResult(generatedBytes []byte, expectedDataFileName string, t *testing.T) bool { 451 | expectedData := make(map[string]interface{}) 452 | generatedData := make(map[string]interface{}) 453 | 454 | expectedBytes, err := readTestFile(expectedDataFileName) 455 | if err != nil { 456 | t.Fatalf("can not read test file '%s': %s", expectedDataFileName, err.Error()) 457 | } 458 | if err = json.Unmarshal(expectedBytes, &expectedData); err != nil { 459 | t.Fatalf("can not unmarshal '%s' data: %s", expectedDataFileName, err.Error()) 460 | } 461 | if err = json.Unmarshal(generatedBytes, &generatedData); err != nil { 462 | t.Fatalf("can not unmarshal generated data: %s", err.Error()) 463 | } 464 | 465 | for _, diff := range pretty.Diff(expectedData, generatedData) { 466 | pretty.Println(diff) 467 | } 468 | 469 | return reflect.DeepEqual(expectedData, generatedData) 470 | } 471 | 472 | func TestGenDocumentFunc(t *testing.T) { 473 | SetHost("localhost:1234") 474 | SetBasePath("/") 475 | SetContact("Test Name", "test@email.com", "http://test.com") 476 | SetLicense("MIT", "http://www.mit.com") 477 | SetInfo("Test API", "Generate api document", "term.com", "1.0.0") 478 | EnableCORS(false) 479 | 480 | info := PathItemInfo{ 481 | Path: "/v1/test/handler", 482 | Title: "TestHandler", 483 | Description: "This is just a test handler with GET request", 484 | Method: "GET", 485 | } 486 | 487 | if err := SetPathItem(info, nil, nil, nil); err != nil { 488 | t.Fatalf("error %v", err) 489 | } 490 | 491 | w := httptest.NewRecorder() 492 | r, err := http.NewRequest("GET", "http://localhost:1234/docs/swagger.json", nil) 493 | if err != nil { 494 | t.Fatalf("error when create request: %v", err) 495 | } 496 | 497 | ServeHTTP(w, r) 498 | 499 | responseDoc := Document{} 500 | if err := json.Unmarshal(w.Body.Bytes(), &responseDoc); err != nil { 501 | t.Fatalf("could not get response: %v", err) 502 | } 503 | 504 | if responseDoc.Host != "localhost:1234" { 505 | t.Fatalf("gen swagger json error: %v", responseDoc) 506 | } 507 | 508 | // generate again without base path 509 | data, _ := GenDocument() 510 | doc := Document{} 511 | if err := json.Unmarshal(data, &doc); err != nil { 512 | t.Fatalf("could not get response: %v", err) 513 | } 514 | 515 | for path := range doc.Paths { 516 | if !strings.HasPrefix(path, "/v1") { 517 | t.Fatal("Path should be started by /v1") 518 | } 519 | } 520 | 521 | assertTrue(w.Header().Get("Access-Control-Allow-Origin") == "", t) 522 | assertTrue(w.Header().Get("Access-Control-Allow-Methods") == "", t) 523 | assertTrue(w.Header().Get("Access-Control-Allow-Headers") == "", t) 524 | } 525 | 526 | func TestCORSSupport(t *testing.T) { 527 | g := NewGenerator() 528 | g.EnableCORS(true, "X-ABC-Test"). 529 | SetHost("localhost:1234") 530 | 531 | info := PathItemInfo{ 532 | Path: "/v1/test/handler", 533 | Title: "TestHandler", 534 | Description: "This is just a test handler with GET request", 535 | Method: "GET", 536 | } 537 | 538 | if err := g.SetPathItem(info, nil, nil, nil); err != nil { 539 | t.Fatalf("error %v", err) 540 | } 541 | 542 | w := httptest.NewRecorder() 543 | r, err := http.NewRequest("GET", "http://localhost:1234/docs/swagger.json", nil) 544 | if err != nil { 545 | t.Fatalf("error when create request: %v", err) 546 | } 547 | 548 | g.ServeHTTP(w, r) 549 | 550 | assertTrue(w.Header().Get("Access-Control-Allow-Origin") == "*", t) 551 | assertTrue(w.Header().Get("Access-Control-Allow-Methods") == "GET, POST, DELETE, PUT, PATCH, OPTIONS", t) 552 | assertTrue(w.Header().Get("Access-Control-Allow-Headers") == "Content-Type, api_key, Authorization, X-ABC-Test", t) 553 | } 554 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 5e409c7f529b17ec65ea5791642bec10bad3864c2a295df68046c1f84dc6ee58 2 | updated: 2017-10-02T17:10:47.716692286+08:00 3 | imports: [] 4 | testImports: 5 | - name: github.com/kr/pretty 6 | version: cfb55aafdaf3ec08f0db22699ab822c50091b1c4 7 | - name: github.com/kr/text 8 | version: bb797dc4fb8320488f47bf11de07a733d7233e1f 9 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/lazada/swgen 2 | import: [] 3 | testImport: 4 | - package: github.com/kr/pretty 5 | -------------------------------------------------------------------------------- /helper.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // ReflectTypeHash returns private (unexported) `hash` field of the Golang internal reflect.rtype struct for a given reflect.Type 9 | // This hash is used to (quasi-)uniquely identify a reflect.Type value 10 | func ReflectTypeHash(t reflect.Type) uint32 { 11 | return uint32(reflect.Indirect(reflect.ValueOf(t)).FieldByName("hash").Uint()) 12 | } 13 | 14 | // ReflectTypeReliableName returns real name of given reflect.Type, if it is non-empty, or auto-generates "anon_*"] 15 | // name for anonymous structs 16 | func ReflectTypeReliableName(t reflect.Type) string { 17 | if t.Name() != "" { 18 | return t.Name() 19 | } 20 | return fmt.Sprintf("anon_%08x", ReflectTypeHash(t)) 21 | } 22 | -------------------------------------------------------------------------------- /helper_test.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | type TestStruct1 struct { 9 | ID uint 10 | Name string 11 | } 12 | 13 | type TestStruct2 struct { 14 | ID uint 15 | Name string 16 | } 17 | 18 | func TestReflectTypeHash(t *testing.T) { 19 | var ( 20 | ts1a, ts1b TestStruct1 21 | ts2 TestStruct2 22 | 23 | anon1a, anon1b struct { 24 | ID uint 25 | Name string 26 | } 27 | 28 | anon2 = struct { 29 | ID uint 30 | Name string 31 | }{} 32 | ) 33 | 34 | if reflect.TypeOf(ts1a) != reflect.TypeOf(ts1b) { 35 | t.Error("Different reflect.Type on instances of the same named struct") 36 | } 37 | 38 | if ReflectTypeHash(reflect.TypeOf(ts1a)) == ReflectTypeHash(reflect.TypeOf(ts2)) { 39 | t.Error("Same reflect.Type on instances of different named structs:", ReflectTypeHash(reflect.TypeOf(ts1a))) 40 | } 41 | 42 | if reflect.TypeOf(anon1a) != reflect.TypeOf(anon1b) { 43 | t.Error("Different reflect.Type on instances of the same anonymous struct") 44 | } 45 | 46 | if ReflectTypeHash(reflect.TypeOf(anon1a)) != ReflectTypeHash(reflect.TypeOf(anon2)) { 47 | t.Error("Different reflect.Type on instances of the different anonymous structs with same fields") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "encoding" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "reflect" 9 | "regexp" 10 | "strconv" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | const ( 16 | refDefinitionPrefix = "#/definitions/" 17 | ) 18 | 19 | var ( 20 | typeOfJSONRawMsg = reflect.TypeOf((*json.RawMessage)(nil)).Elem() 21 | typeOfTime = reflect.TypeOf((*time.Time)(nil)).Elem() 22 | typeOfTextUnmarshaler = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 23 | ) 24 | 25 | // IParameter allows to return custom parameters 26 | type IParameter interface { 27 | SwgenParameter() (name string, params []ParamObj, err error) 28 | } 29 | 30 | // IDefinition allows to return custom definitions 31 | type IDefinition interface { 32 | SwgenDefinition() (typeName string, typeDef SchemaObj, err error) 33 | } 34 | 35 | func (g *Generator) addDefinition(t reflect.Type, typeDef *SchemaObj) { 36 | if typeDef.TypeName == "" { 37 | return // there should be no anonymous definitions in Swagger JSON 38 | } 39 | if _, ok := g.definitions[t]; ok { // skip existing definition 40 | return 41 | } 42 | 43 | if _, ok := g.definitionAdded[typeDef.TypeName]; ok { // process duplicate TypeName 44 | var typeName string 45 | typeIndex := 2 46 | for { 47 | typeName = fmt.Sprintf("%sType%d", typeDef.TypeName, typeIndex) 48 | if _, ok := g.definitionAdded[typeName]; !ok { 49 | break 50 | } 51 | typeIndex++ 52 | } 53 | 54 | typeDef.TypeName = typeName 55 | if typeDef.Ref != "" { 56 | typeDef.Ref = refDefinitionPrefix + typeDef.TypeName 57 | } 58 | } 59 | g.definitionAdded[typeDef.TypeName] = true 60 | g.definitions[t] = *typeDef 61 | } 62 | 63 | func (g *Generator) defExists(t reflect.Type) (b bool) { 64 | _, b = g.definitions[t] 65 | return b 66 | } 67 | 68 | func (g *Generator) addToDefQueue(t reflect.Type) { 69 | g.defQueue[t] = struct{}{} 70 | } 71 | 72 | func (g *Generator) defInQueue(t reflect.Type) (found bool) { 73 | _, found = g.defQueue[t] 74 | return 75 | } 76 | 77 | func (g *Generator) getDefinition(t reflect.Type) (typeDef SchemaObj, found bool) { 78 | typeDef, found = g.definitions[t] 79 | if !found && t.Kind() == reflect.Ptr { 80 | typeDef, found = g.definitions[t.Elem()] 81 | } 82 | return 83 | } 84 | 85 | func (g *Generator) deleteDefinition(t reflect.Type) { 86 | delete(g.definitions, t) 87 | } 88 | 89 | // 90 | // Parse swagger schema object 91 | // see http://swagger.io/specification/#schemaObject 92 | // 93 | 94 | // ResetDefinitions will remove all exists definitions and init again 95 | func (g *Generator) ResetDefinitions() { 96 | g.definitions = make(defMap) 97 | g.definitionAdded = make(map[string]bool) 98 | g.defQueue = make(map[reflect.Type]struct{}) 99 | } 100 | 101 | // ResetDefinitions will remove all exists definitions and init again 102 | func ResetDefinitions() { 103 | gen.ResetDefinitions() 104 | } 105 | 106 | // ParseDefinition create a DefObj from input object, it should be a non-nil pointer to anything 107 | // it reuse schema/json tag for property name. 108 | func (g *Generator) ParseDefinition(i interface{}) (schema SchemaObj, err error) { 109 | var ( 110 | typeName string 111 | typeDef SchemaObj 112 | t = reflect.TypeOf(i) 113 | v = reflect.ValueOf(i) 114 | ) 115 | 116 | if mappedTo, ok := g.getMappedType(t); ok { 117 | typeName = t.Name() 118 | t = reflect.TypeOf(mappedTo) 119 | v = reflect.ValueOf(mappedTo) 120 | } 121 | 122 | if definition, ok := i.(IDefinition); ok { 123 | typeName, typeDef, err = definition.SwgenDefinition() 124 | if err != nil { 125 | return typeDef, err 126 | } 127 | if typeName == "" { 128 | typeName = t.Name() 129 | } 130 | typeDef.TypeName = typeName 131 | if def, ok := g.getDefinition(t); ok { 132 | return SchemaObj{Ref: refDefinitionPrefix + def.TypeName, TypeName: def.TypeName}, nil 133 | } 134 | defer g.parseDefInQueue() 135 | if g.reflectGoTypes { 136 | typeDef.GoType = goType(t) 137 | } 138 | g.addDefinition(t, &typeDef) 139 | 140 | return SchemaObj{Ref: refDefinitionPrefix + typeDef.TypeName, TypeName: typeDef.TypeName}, nil 141 | } 142 | 143 | if t.Kind() == reflect.Ptr { 144 | t = t.Elem() 145 | } 146 | 147 | switch t.Kind() { 148 | case reflect.Struct: 149 | if typeDef, found := g.getDefinition(t); found { 150 | return typeDef.Export(), nil 151 | } 152 | 153 | typeDef = *NewSchemaObj("object", ReflectTypeReliableName(t)) 154 | typeDef.Properties = g.parseDefinitionProperties(v, &typeDef) 155 | if typeDef.TypeName == "" { 156 | typeDef.TypeName = typeName 157 | } 158 | 159 | //if len(typeDef.Properties) == 0 { 160 | // typeDef.Ref = "" 161 | //} 162 | case reflect.Slice, reflect.Array: 163 | elemType := t.Elem() 164 | if elemType.Kind() == reflect.Ptr { 165 | elemType = elemType.Elem() 166 | } 167 | 168 | if typeDef, found := g.getDefinition(t); found { 169 | return typeDef.Export(), nil 170 | } 171 | 172 | var itemSchema SchemaObj 173 | if elemType.Kind() != reflect.Struct || (elemType.Kind() == reflect.Struct && elemType.Name() != "") { 174 | itemSchema = g.genSchemaForType(elemType) 175 | } else { 176 | itemSchema = *NewSchemaObj("object", elemType.Name()) 177 | itemSchema.Properties = g.parseDefinitionProperties(v.Elem(), &itemSchema) 178 | } 179 | 180 | typeDef = *NewSchemaObj("array", t.Name()) 181 | typeDef.Items = &itemSchema 182 | if typeDef.TypeName == "" { 183 | typeDef.TypeName = typeName 184 | } 185 | case reflect.Map: 186 | elemType := t.Elem() 187 | if elemType.Kind() == reflect.Ptr { 188 | elemType = elemType.Elem() 189 | } 190 | 191 | if typeDef, found := g.getDefinition(t); found { 192 | return typeDef.Export(), nil 193 | } 194 | 195 | typeDef = *NewSchemaObj("object", t.Name()) 196 | itemDef := g.genSchemaForType(elemType) 197 | typeDef.AdditionalProperties = &itemDef 198 | if typeDef.TypeName == "" { 199 | typeDef.TypeName = typeName 200 | } 201 | default: 202 | typeDef = g.genSchemaForType(t) 203 | typeDef.TypeName = typeDef.Type 204 | return typeDef, nil 205 | } 206 | 207 | defer g.parseDefInQueue() 208 | 209 | if g.reflectGoTypes { 210 | typeDef.GoType = goType(t) 211 | } 212 | 213 | if typeDef.TypeName != "" { // non-anonymous types should be added to definitions map and returned "in-place" as references 214 | g.addDefinition(t, &typeDef) 215 | return typeDef.Export(), nil 216 | } 217 | return typeDef, nil // anonymous types are not added to definitions map; instead, they are returned "in-place" in full form 218 | } 219 | 220 | func goType(t reflect.Type) (s string) { 221 | s = t.Name() 222 | pkgPath := t.PkgPath() 223 | if pkgPath != "" { 224 | pos := strings.Index(pkgPath, "/vendor/") 225 | if pos != -1 { 226 | pkgPath = pkgPath[pos+8:] 227 | } 228 | s = pkgPath + "." + s 229 | } 230 | 231 | ts := t.String() 232 | typeRef := s 233 | 234 | pos := strings.LastIndex(typeRef, "/") 235 | if pos != -1 { 236 | typeRef = typeRef[pos+1:] 237 | } 238 | 239 | if typeRef != ts { 240 | s = s + "::" + t.String() 241 | } 242 | 243 | switch t.Kind() { 244 | case reflect.Slice: 245 | return "[]" + goType(t.Elem()) 246 | case reflect.Ptr: 247 | return "*" + goType(t.Elem()) 248 | case reflect.Map: 249 | return "map[" + goType(t.Key()) + "]" + goType(t.Elem()) 250 | } 251 | 252 | return 253 | } 254 | 255 | func (g *Generator) parseDefinitionProperties(v reflect.Value, parent *SchemaObj) map[string]SchemaObj { 256 | if v.Kind() == reflect.Ptr { 257 | v = v.Elem() 258 | } 259 | t := v.Type() 260 | properties := make(map[string]SchemaObj, t.NumField()) 261 | if g.reflectGoTypes && parent.GoPropertyNames == nil { 262 | parent.GoPropertyNames = make(map[string]string, t.NumField()) 263 | parent.GoPropertyTypes = make(map[string]string, t.NumField()) 264 | } 265 | 266 | for i := 0; i < t.NumField(); i = i + 1 { 267 | field := t.Field(i) 268 | 269 | // we can't access the value of un-exportable field 270 | if field.PkgPath != "" { 271 | continue 272 | } 273 | 274 | if field.Anonymous { 275 | fieldProperties := g.parseDefinitionProperties(v.Field(i), parent) 276 | for propertyName, property := range fieldProperties { 277 | properties[propertyName] = property 278 | } 279 | continue 280 | } 281 | 282 | // don't check if it's omitted 283 | var tag string 284 | if tag = field.Tag.Get("json"); tag == "-" || tag == "" { 285 | continue 286 | } 287 | 288 | propName := strings.Split(tag, ",")[0] 289 | var ( 290 | obj SchemaObj 291 | ) 292 | 293 | if dataType := field.Tag.Get("swgen_type"); dataType != "" { 294 | obj = SchemaFromCommonName(commonName(dataType)) 295 | } else { 296 | if field.Type.Kind() == reflect.Interface && v.Field(i).Elem().IsValid() { 297 | obj = g.genSchemaForType(v.Field(i).Elem().Type()) 298 | } else { 299 | obj = g.genSchemaForType(field.Type) 300 | } 301 | } 302 | 303 | if defaultTag := field.Tag.Get("default"); defaultTag != "" { 304 | if defaultValue, err := g.caseDefaultValue(field.Type, defaultTag); err == nil { 305 | obj.Default = defaultValue 306 | } 307 | } 308 | if g.reflectGoTypes { 309 | if obj.Ref == "" { 310 | obj.GoType = goType(field.Type) 311 | } 312 | parent.GoPropertyNames[propName] = field.Name 313 | parent.GoPropertyTypes[propName] = goType(field.Type) 314 | } 315 | 316 | properties[propName] = obj 317 | } 318 | 319 | return properties 320 | } 321 | 322 | func (g *Generator) caseDefaultValue(t reflect.Type, defaultValue string) (interface{}, error) { 323 | for t.Kind() == reflect.Ptr { 324 | t = t.Elem() 325 | } 326 | 327 | kind := t.Kind() 328 | 329 | switch kind { 330 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 331 | return strconv.ParseInt(defaultValue, 10, 64) 332 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 333 | return strconv.ParseUint(defaultValue, 10, 64) 334 | case reflect.Float32, reflect.Float64: 335 | return strconv.ParseFloat(defaultValue, 64) 336 | case reflect.String: 337 | return defaultValue, nil 338 | case reflect.Bool: 339 | return strconv.ParseBool(defaultValue) 340 | default: 341 | instance := reflect.New(t).Interface() 342 | if err := json.Unmarshal([]byte(defaultValue), instance); err != nil { 343 | return nil, err 344 | } 345 | return reflect.Indirect(reflect.ValueOf(instance)).Interface(), nil 346 | } 347 | } 348 | 349 | // ParseDefinition create a DefObj from input object, it should be a pointer to a struct, 350 | // it reuse schema/json tag for property name. 351 | func ParseDefinition(i interface{}) (typeDef SchemaObj, err error) { 352 | return gen.ParseDefinition(i) 353 | } 354 | 355 | func (g *Generator) parseDefInQueue() { 356 | if len(g.defQueue) == 0 { 357 | return 358 | } 359 | 360 | for t := range g.defQueue { 361 | g.ParseDefinition(reflect.Zero(t).Interface()) 362 | } 363 | } 364 | 365 | func (g *Generator) genSchemaForType(t reflect.Type) SchemaObj { 366 | for t.Kind() == reflect.Ptr { 367 | t = t.Elem() 368 | } 369 | 370 | smObj := SchemaObj{TypeName: t.Name()} 371 | 372 | switch t.Kind() { 373 | case reflect.Bool: 374 | smObj = SchemaFromCommonName(CommonNameBoolean) 375 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16: 376 | smObj = SchemaFromCommonName(CommonNameInteger) 377 | case reflect.Int64, reflect.Uint32, reflect.Uint64: 378 | smObj = SchemaFromCommonName(CommonNameLong) 379 | case reflect.Float32: 380 | smObj = SchemaFromCommonName(CommonNameFloat) 381 | case reflect.Float64: 382 | smObj = SchemaFromCommonName(CommonNameDouble) 383 | case reflect.String: 384 | smObj = SchemaFromCommonName(CommonNameString) 385 | case reflect.Array, reflect.Slice: 386 | if t != typeOfJSONRawMsg { 387 | smObj.Type = "array" 388 | itemSchema := g.genSchemaForType(t.Elem()) 389 | smObj.Items = &itemSchema 390 | } 391 | case reflect.Map: 392 | smObj.Type = "object" 393 | itemSchema := g.genSchemaForType(t.Elem()) 394 | smObj.AdditionalProperties = &itemSchema 395 | case reflect.Struct: 396 | switch { 397 | case t == typeOfTime: 398 | smObj = SchemaFromCommonName(CommonNameDateTime) 399 | case reflect.PtrTo(t).Implements(typeOfTextUnmarshaler): 400 | smObj.Type = "string" 401 | default: 402 | name := ReflectTypeReliableName(t) 403 | smObj.Ref = refDefinitionPrefix + name 404 | if !g.defExists(t) || !g.defInQueue(t) { 405 | g.addToDefQueue(t) 406 | } 407 | } 408 | case reflect.Interface: 409 | if t.NumMethod() > 0 { 410 | panic("Non-empty interface is not supported: " + t.String()) 411 | } 412 | default: 413 | panic(fmt.Sprintf("type %s is not supported: %s", t.Kind(), t.String())) 414 | } 415 | 416 | if g.reflectGoTypes && smObj.Ref == "" { 417 | smObj.GoType = goType(t) 418 | } 419 | 420 | return smObj 421 | } 422 | 423 | // 424 | // Parse struct to swagger parameter object of operation object 425 | // see http://swagger.io/specification/#parameterObject 426 | // 427 | 428 | // ParseParameter parse input struct to swagger parameter object 429 | func (g *Generator) ParseParameter(i interface{}) (name string, params []ParamObj, err error) { 430 | if param, ok := i.(IParameter); ok { 431 | return param.SwgenParameter() 432 | } 433 | 434 | v := reflect.ValueOf(i) 435 | 436 | if v.Kind() == reflect.Ptr { 437 | v = v.Elem() 438 | } 439 | 440 | if v.Kind() != reflect.Struct { 441 | err = errors.New("Generator.ParseParameter() failed: parameters must be a struct") 442 | return 443 | } 444 | 445 | t := v.Type() 446 | 447 | if mappedTo, ok := g.getMappedType(t); ok { 448 | return g.ParseParameter(mappedTo) 449 | } 450 | 451 | name = t.Name() 452 | params = []ParamObj{} 453 | 454 | for i := 0; i < t.NumField(); i = i + 1 { 455 | field := t.Field(i) 456 | // we can't access the value of un-exportable or anonymous fields 457 | if field.PkgPath != "" || field.Anonymous { 458 | continue 459 | } 460 | 461 | // don't check if it's omitted 462 | var nameTag string 463 | 464 | var inPath bool 465 | if nameTag = field.Tag.Get("schema"); nameTag == "-" || nameTag == "" { 466 | inPath = true 467 | if nameTag = field.Tag.Get("path"); nameTag == "-" || nameTag == "" { 468 | continue 469 | } 470 | } 471 | 472 | paramName := strings.Split(nameTag, ",")[0] 473 | param := ParamObj{} 474 | if g.reflectGoTypes { 475 | param.AddExtendedField("x-go-name", field.Name) 476 | param.AddExtendedField("x-go-type", goType(field.Type)) 477 | } 478 | 479 | param.Name = paramName 480 | 481 | if e, isEnumer := reflect.Zero(field.Type).Interface().(enumer); isEnumer { 482 | param.Enum.Enum, param.Enum.EnumNames = e.GetEnumSlices() 483 | } 484 | 485 | if descTag := field.Tag.Get("description"); descTag != "-" && descTag != "" { 486 | param.Description = descTag 487 | } 488 | 489 | if reqTag := field.Tag.Get("required"); reqTag == "-" || reqTag == "false" { 490 | param.Required = false 491 | } else { 492 | param.Required = true 493 | } 494 | 495 | if inTag := field.Tag.Get("in"); inTag != "-" && inTag != "" { 496 | param.In = inTag // todo: validate IN value 497 | } else if inPath { 498 | param.In = "path" 499 | } else { 500 | param.In = "query" 501 | } 502 | 503 | var schema SchemaObj 504 | if swGenType := field.Tag.Get("swgen_type"); swGenType != "" { 505 | schema = SchemaFromCommonName(commonName(swGenType)) 506 | } else { 507 | if mappedTo, ok := g.getMappedType(field.Type); ok { 508 | schema = g.genSchemaForType(reflect.TypeOf(mappedTo)) 509 | } else { 510 | schema = g.genSchemaForType(field.Type) 511 | } 512 | } 513 | 514 | if schema.Type == "" { 515 | panic("dont support struct " + v.Type().Name() + " in property " + field.Name + " of parameter struct") 516 | } 517 | 518 | param.Type = schema.Type 519 | param.Format = schema.Format 520 | 521 | if schema.Type == "array" && schema.Items != nil { 522 | if schema.Items.Ref != "" || schema.Items.Type == "array" { 523 | panic("dont support array of struct or nested array in parameter") 524 | } 525 | 526 | param.Items = &ParamItemObj{ 527 | Type: schema.Items.Type, 528 | Format: schema.Items.Format, 529 | } 530 | param.CollectionFormat = "multi" // default for now 531 | } 532 | 533 | params = append(params, param) 534 | } 535 | 536 | return 537 | } 538 | 539 | // ParseParameter parse input struct to swagger parameter object 540 | func ParseParameter(i interface{}) (name string, params []ParamObj, err error) { 541 | return gen.ParseParameter(i) 542 | } 543 | 544 | // ResetPaths remove all current paths 545 | func (g *Generator) ResetPaths() { 546 | g.paths = make(map[string]PathItem) 547 | } 548 | 549 | // ResetPaths remove all current paths 550 | func ResetPaths() { 551 | gen.ResetPaths() 552 | } 553 | 554 | var regexFindPathParameter = regexp.MustCompile(`\{([^}:]+)(:[^\/]+)?(?:\})`) 555 | 556 | // SetPathItem register path item with some information and input, output 557 | func (g *Generator) SetPathItem(info PathItemInfo, params interface{}, body interface{}, response interface{}) error { 558 | var ( 559 | item PathItem 560 | found bool 561 | ) 562 | 563 | pathParametersSubmatches := regexFindPathParameter.FindAllStringSubmatch(info.Path, -1) 564 | if len(pathParametersSubmatches) > 0 { 565 | for _, submatch := range pathParametersSubmatches { 566 | if submatch[2] != "" { // Remove gorilla.Mux-style regexp in path 567 | info.Path = strings.Replace(info.Path, submatch[0], "{"+submatch[1]+"}", 1) 568 | } 569 | } 570 | } 571 | 572 | item, found = g.paths[info.Path] 573 | 574 | if found && item.HasMethod(info.Method) { 575 | return nil 576 | } 577 | 578 | if !found { 579 | item = PathItem{} 580 | } 581 | 582 | operationObj := &OperationObj{} 583 | operationObj.Summary = info.Title 584 | operationObj.Description = info.Description 585 | operationObj.Deprecated = info.Deprecated 586 | operationObj.additionalData = info.additionalData 587 | if info.Tag != "" { 588 | operationObj.Tags = []string{info.Tag} 589 | } 590 | 591 | operationObj.Security = make([]map[string][]string, 0) 592 | if len(info.Security) > 0 { 593 | for _, sec := range info.Security { 594 | if _, ok := g.doc.SecurityDefinitions[sec]; ok { 595 | operationObj.Security = append(operationObj.Security, map[string][]string{sec: {}}) 596 | } else { 597 | return errors.New("Undefined security definition: " + sec) 598 | } 599 | } 600 | } 601 | 602 | if len(info.SecurityOAuth2) > 0 { 603 | for sec, scopes := range info.SecurityOAuth2 { 604 | if _, ok := g.doc.SecurityDefinitions[sec]; ok { 605 | operationObj.Security = append(operationObj.Security, map[string][]string{sec: scopes}) 606 | } else { 607 | return errors.New("Undefined security definition: " + sec) 608 | } 609 | } 610 | } 611 | 612 | if params != nil { 613 | if g.reflectGoTypes { 614 | operationObj.AddExtendedField("x-request-go-type", goType(reflect.TypeOf(params))) 615 | } 616 | 617 | if _, params, err := g.ParseParameter(params); err == nil { 618 | operationObj.Parameters = params 619 | } else { 620 | return err 621 | } 622 | } 623 | 624 | operationObj.Responses = g.parseResponseObject(response) 625 | 626 | if body != nil { 627 | if g.reflectGoTypes { 628 | operationObj.AddExtendedField("x-request-go-type", goType(reflect.TypeOf(body))) 629 | } 630 | 631 | typeDef, err := g.ParseDefinition(body) 632 | 633 | if err != nil { 634 | return err 635 | } 636 | 637 | if !typeDef.isEmpty() { 638 | param := ParamObj{ 639 | Name: "body", 640 | In: "body", 641 | Required: true, 642 | Schema: &typeDef, 643 | } 644 | 645 | if operationObj.Parameters == nil { 646 | operationObj.Parameters = make([]ParamObj, 0, 1) 647 | } 648 | 649 | operationObj.Parameters = append(operationObj.Parameters, param) 650 | } else { 651 | g.deleteDefinition(reflect.TypeOf(body)) 652 | } 653 | } 654 | 655 | switch strings.ToUpper(info.Method) { 656 | case "GET": 657 | item.Get = operationObj 658 | case "POST": 659 | item.Post = operationObj 660 | case "PUT": 661 | item.Put = operationObj 662 | case "DELETE": 663 | item.Delete = operationObj 664 | case "OPTIONS": 665 | item.Options = operationObj 666 | case "HEAD": 667 | item.Head = operationObj 668 | case "PATCH": 669 | item.Patch = operationObj 670 | } 671 | 672 | g.paths[info.Path] = item 673 | 674 | return nil 675 | } 676 | 677 | // SetPathItem register path item with some information and input, output 678 | func SetPathItem(info PathItemInfo, params interface{}, body interface{}, response interface{}) error { 679 | return gen.SetPathItem(info, params, body, response) 680 | } 681 | 682 | func (g *Generator) parseResponseObject(responseObj interface{}) (res Responses) { 683 | res = make(Responses) 684 | 685 | if responseObj != nil { 686 | schema, err := g.ParseDefinition(responseObj) 687 | if err != nil { 688 | panic(fmt.Sprintf("could not create schema object for response %v", responseObj)) 689 | } 690 | // since we only response json object 691 | // so, type of response object is always object 692 | res["200"] = ResponseObj{ 693 | Description: "request success", 694 | Schema: &schema, 695 | } 696 | } else { 697 | res["200"] = ResponseObj{ 698 | Description: "request success", 699 | Schema: &SchemaObj{Type: "null"}, 700 | } 701 | } 702 | 703 | return res 704 | } 705 | -------------------------------------------------------------------------------- /parser_test.go: -------------------------------------------------------------------------------- 1 | package swgen 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "reflect" 7 | "sync" 8 | "testing" 9 | ) 10 | 11 | type Person struct { 12 | Name *PersonName `json:"name"` 13 | SecondName PersonName `json:"second_name"` 14 | Age uint `json:"age"` 15 | Children []Person `json:"children"` 16 | Tags []string `json:"tags"` 17 | Weight float64 `json:"weight"` 18 | Active bool `json:"active"` 19 | Balance float32 `json:"balance"` 20 | } 21 | 22 | type PersonName struct { 23 | First string `json:"first_name"` 24 | Middle string `json:"middle_name"` 25 | Last string `json:"last_name"` 26 | Nickname string `schema:"-"` 27 | _ string 28 | } 29 | 30 | type Employee struct { 31 | Person 32 | Salary float64 `json:"salary"` 33 | } 34 | 35 | type Project struct { 36 | ID uint `json:"id"` 37 | Name string `json:"name"` 38 | Manager interface{} `json:"manager"` 39 | } 40 | 41 | // PreferredWarehouseRequest is request object of get preferred warehouse handler 42 | type PreferredWarehouseRequest struct { 43 | Items []string `schema:"items" description:"List of simple sku"` 44 | IDCustomerLocation uint64 `schema:"id_customer_location" description:"-"` 45 | } 46 | 47 | func TestResetDefinitions(t *testing.T) { 48 | ts := &Person{} 49 | if _, err := ParseDefinition(ts); err != nil { 50 | t.Fatalf("%v", err) 51 | } 52 | 53 | if len(gen.definitions) == 0 { 54 | t.Fatalf("len of gen.definitions must be greater than 0") 55 | } 56 | 57 | ResetDefinitions() 58 | if len(gen.definitions) != 0 { 59 | t.Fatalf("len of gen.definitions must be equal to 0") 60 | } 61 | } 62 | 63 | func TestParseDefinition(t *testing.T) { 64 | ts := &Person{} 65 | if _, err := ParseDefinition(ts); err != nil { 66 | t.Fatalf("%v", err) 67 | } 68 | } 69 | 70 | func TestParseDefinitionEmptyInterface(t *testing.T) { 71 | var ts interface{} 72 | if _, err := ParseDefinition(&ts); err != nil { 73 | t.Fatalf("%v", err) 74 | } 75 | } 76 | 77 | func TestParseDefinitionNonEmptyInterface(t *testing.T) { 78 | defer func() { 79 | if r := recover(); r == nil { 80 | t.Errorf("Panic expected for non-empty interface") 81 | } 82 | }() 83 | 84 | var ts interface { 85 | Test() 86 | } 87 | 88 | if _, err := ParseDefinition(&ts); err != nil { 89 | t.Fatalf("%v", err) 90 | } 91 | } 92 | 93 | func TestParseDefinitionWithEmbeddedStruct(t *testing.T) { 94 | ts := &Employee{} 95 | tt := reflect.TypeOf(ts) 96 | 97 | if _, err := ParseDefinition(ts); err != nil { 98 | t.Fatalf("%v", err) 99 | } 100 | 101 | if typeDef, found := gen.getDefinition(tt); found == false { 102 | t.Fatal("No definition for", tt) 103 | } else { 104 | propertiesCount := len(typeDef.Properties) 105 | expectedPropertiesCount := 9 106 | if propertiesCount != expectedPropertiesCount { 107 | t.Fatalf("Expected %d properties, got %d : %#v", expectedPropertiesCount, propertiesCount, typeDef.Properties) 108 | } 109 | } 110 | } 111 | 112 | func TestParseDefinitionWithEmbeddedInterface(t *testing.T) { 113 | p := &Project{Manager: new(Employee)} 114 | tt := reflect.TypeOf(p) 115 | 116 | if _, err := ParseDefinition(p); err != nil { 117 | t.Fatalf("%v", err) 118 | } 119 | 120 | if typeDef, found := gen.getDefinition(tt); found == false { 121 | t.Fatal("No definition for", tt) 122 | } else { 123 | if typeDef.Properties["manager"].Ref != "#/definitions/Employee" { 124 | t.Fatalf("'manager' field was not parsed correctly.") 125 | } 126 | } 127 | } 128 | 129 | func TestParseDefinitionString(t *testing.T) { 130 | typeDef, err := ParseDefinition("string") 131 | name := typeDef.TypeName 132 | if err != nil { 133 | t.Fatalf("Error parsing string: %+v", err) 134 | } 135 | if name != "string" { 136 | t.Fatalf("Wrong type name. Expect %q, got %q", "string", name) 137 | } 138 | } 139 | 140 | func TestParseDefinitionArray(t *testing.T) { 141 | type Names []string 142 | typeDef, err := ParseDefinition(Names{}) 143 | if err != nil { 144 | t.Fatalf("Error while parsing array of string: %v", err) 145 | } 146 | 147 | if typeDef.TypeName != "Names" { 148 | t.Fatalf("Wrong type name. Expected: Names, Obtained: %v", typeDef.TypeName) 149 | } 150 | 151 | // re-parse with pointer input 152 | // should get from definition list 153 | _, err = ParseDefinition(&Names{}) 154 | if err != nil { 155 | t.Fatalf("Error while parsing array of string: %v", err) 156 | } 157 | 158 | // try to parse a named map 159 | type MapList map[string]string 160 | _, err = ParseDefinition(&MapList{}) 161 | if err != nil { 162 | t.Fatalf("Error while parsing map string to string: %v", err) 163 | } 164 | 165 | // named array of object 166 | type Person struct{} 167 | type Persons []*Person 168 | _, err = ParseDefinition(&Persons{}) 169 | if err != nil { 170 | t.Fatalf("Error while parsing array of object: %v", err) 171 | } 172 | } 173 | 174 | func TestParseParameter(t *testing.T) { 175 | p := &PreferredWarehouseRequest{} 176 | name, params, err := ParseParameter(p) 177 | 178 | if err != nil { 179 | t.Fatalf("error %v", err) 180 | } 181 | 182 | if name != "PreferredWarehouseRequest" { 183 | t.Fatalf("name of parameter is %s, expected is PreferredWarehouseRequest", name) 184 | } 185 | 186 | if len(params) != 2 { 187 | t.Fatalf("number of parameter should be 2") 188 | } 189 | } 190 | 191 | func TestParseParameterError(t *testing.T) { 192 | _, _, err := ParseParameter(true) 193 | if err == nil { 194 | t.Fatalf("it should return error") 195 | } 196 | } 197 | 198 | // 199 | // test and data for TestSetPathItem 200 | // 201 | 202 | func TestSetPathItem(t *testing.T) { 203 | h := &testHandler{} 204 | 205 | methods := []string{"GET", "POST", "HEAD", "PUT", "OPTIONS", "DELETE", "PATCH"} 206 | 207 | for _, method := range methods { 208 | info := PathItemInfo{ 209 | Path: "/v1/test/handler", 210 | Title: "TestHandler", 211 | Description: fmt.Sprintf("This is just a test handler with %s request", method), 212 | Method: method, 213 | } 214 | err := SetPathItem(info, h.GetRequestBuffer(method), h.GetBodyBuffer(), h.GetResponseBuffer(method)) 215 | if err != nil { 216 | t.Fatalf("error %v", err) 217 | } 218 | } 219 | } 220 | 221 | func TestResetPaths(t *testing.T) { 222 | TestSetPathItem(t) 223 | 224 | if len(gen.paths) == 0 { 225 | t.Fatalf("len of gen.paths must be greater than 0") 226 | } 227 | 228 | ResetPaths() 229 | if len(gen.paths) != 0 { 230 | t.Fatalf("len of gen.paths must be equal to 0") 231 | } 232 | } 233 | 234 | // 235 | // benchmark and parallel testing 236 | // 237 | 238 | func BenchmarkParseDefinitionsParallel(b *testing.B) { 239 | var ( 240 | mu sync.Mutex 241 | i int 242 | ) 243 | 244 | data := []interface{}{ 245 | &Person{}, 246 | &PersonName{}, 247 | &PreferredWarehouseRequest{}, 248 | } 249 | 250 | b.SetParallelism(10) 251 | b.RunParallel(func(pb *testing.PB) { 252 | for pb.Next() { 253 | mu.Lock() 254 | i++ 255 | input := data[i%3] 256 | mu.Unlock() 257 | 258 | if _, err := ParseDefinition(input); err != nil { 259 | b.Fatalf("%v", err) 260 | } 261 | } 262 | }) 263 | } 264 | 265 | func BenchmarkSetPathItem(b *testing.B) { 266 | h := &testHandler{} 267 | 268 | infos := []PathItemInfo{ 269 | { 270 | Path: "/v1/test/handler", 271 | Title: "TestHandler", 272 | Description: "This is just a test handler with GET request", 273 | Method: "GET", 274 | }, 275 | { 276 | Path: "/v1/test/handler", 277 | Title: "TestHandler", 278 | Description: "This is just a test handler with POST reqest", 279 | Method: "POST", 280 | }, 281 | } 282 | 283 | var ( 284 | mu sync.Mutex 285 | i int 286 | ) 287 | 288 | b.SetParallelism(10) 289 | b.RunParallel(func(pb *testing.PB) { 290 | for pb.Next() { 291 | mu.Lock() 292 | i++ 293 | info := infos[i%2] 294 | mu.Unlock() 295 | 296 | err := SetPathItem( 297 | info, 298 | h.GetRequestBuffer(info.Method), 299 | h.GetBodyBuffer(), 300 | h.GetResponseBuffer(info.Method), 301 | ) 302 | 303 | if err != nil { 304 | b.Fatalf("error %v", err) 305 | } 306 | } 307 | }) 308 | } 309 | 310 | // testHandler can handle POST and GET request 311 | type testHandler struct{} 312 | 313 | func (th *testHandler) GetName() string { 314 | return "TestHandle" 315 | } 316 | 317 | func (th *testHandler) GetDescription() string { 318 | return "This handler for test ParsePathItem" 319 | } 320 | 321 | func (th *testHandler) GetVersion() string { 322 | return "v1" 323 | } 324 | 325 | func (th *testHandler) GetRoute() string { 326 | return "/test/handler" 327 | } 328 | 329 | func (th *testHandler) GetRequestBuffer(_ string) interface{} { 330 | return &PersonName{} 331 | } 332 | 333 | func (th *testHandler) GetResponseBuffer(method string) interface{} { 334 | if method == "GET" { 335 | return nil 336 | } 337 | 338 | return &PreferredWarehouseRequest{} 339 | } 340 | 341 | func (th *testHandler) GetBodyBuffer() interface{} { 342 | return &Person{} 343 | } 344 | 345 | func (th *testHandler) HandlePost(_ interface{}, _ interface{}) (response interface{}, err error) { 346 | // yes, I can handle a POST request 347 | return 348 | } 349 | 350 | func (th *testHandler) HandleGet(_ interface{}) (response interface{}, err error) { 351 | // yes, I can handle a GET request 352 | return 353 | } 354 | 355 | // 356 | // Test helper 357 | // 358 | 359 | func getReadableJSON(i interface{}, t *testing.T) []byte { 360 | data, err := json.MarshalIndent(i, "", " ") 361 | if err != nil { 362 | t.Fatalf("error while parsing struct to JSON string: %v", err) 363 | } 364 | 365 | return data 366 | } 367 | -------------------------------------------------------------------------------- /sample/sample.go: -------------------------------------------------------------------------------- 1 | package sample 2 | 3 | type TestSampleStruct struct { 4 | SimpleFloat64 float64 `json:"simple_float64"` 5 | SimpleBool bool `json:"simple_bool"` 6 | } 7 | -------------------------------------------------------------------------------- /testdata/test_JSON-RPC.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "swgen title", 5 | "description": "swgen description", 6 | "termsOfService": "term", 7 | "contact": { 8 | "name": "Dylan Noblitt", 9 | "url": "http://example.com", 10 | "email": "dylan.noblitt@example.com" 11 | }, 12 | "license": { 13 | "name": "BEER-WARE", 14 | "url": "https://fedoraproject.org/wiki/Licensing/Beerware" 15 | }, 16 | "version": "2.0" 17 | }, 18 | "host": "localhost", 19 | "basePath": "/", 20 | "schemes": [ 21 | "http", 22 | "https" 23 | ], 24 | "paths": { 25 | "/V1/anonymous1": { 26 | "post": { 27 | "tags": [ 28 | "v1" 29 | ], 30 | "summary": "test10 name", 31 | "description": "test10 description", 32 | "responses": { 33 | "200": { 34 | "description": "request success", 35 | "schema": { 36 | "type": "object", 37 | "additionalProperties": { 38 | "type": "integer", 39 | "format": "int64" 40 | } 41 | } 42 | } 43 | } 44 | } 45 | }, 46 | "/V1/anonymous2": { 47 | "post": { 48 | "tags": [ 49 | "v1" 50 | ], 51 | "summary": "test11 name", 52 | "description": "test11 description", 53 | "responses": { 54 | "200": { 55 | "description": "request success", 56 | "schema": { 57 | "type": "object", 58 | "additionalProperties": { 59 | "type": "string" 60 | } 61 | } 62 | } 63 | } 64 | } 65 | }, 66 | "/V1/anonymous3": { 67 | "post": { 68 | "tags": [ 69 | "v1" 70 | ], 71 | "summary": "test12 name", 72 | "description": "test12 description", 73 | "responses": { 74 | "200": { 75 | "description": "request success", 76 | "schema": { 77 | "type": "array", 78 | "items": { 79 | "type": "string" 80 | } 81 | } 82 | } 83 | } 84 | } 85 | }, 86 | "/V1/anonymous4": { 87 | "post": { 88 | "tags": [ 89 | "v1" 90 | ], 91 | "summary": "test13 name", 92 | "description": "test13 description", 93 | "responses": { 94 | "200": { 95 | "description": "request success", 96 | "schema": { 97 | "type": "array", 98 | "items": { 99 | "type": "integer", 100 | "format": "int32" 101 | } 102 | } 103 | } 104 | } 105 | } 106 | }, 107 | "/V1/anonymous5": { 108 | "post": { 109 | "tags": [ 110 | "v1" 111 | ], 112 | "summary": "test14 name", 113 | "description": "test14 description", 114 | "responses": { 115 | "200": { 116 | "description": "request success", 117 | "schema": { 118 | "type": "string" 119 | } 120 | } 121 | } 122 | } 123 | }, 124 | "/V1/anonymous6": { 125 | "post": { 126 | "tags": [ 127 | "v1" 128 | ], 129 | "summary": "test15 name", 130 | "description": "test15 description", 131 | "responses": { 132 | "200": { 133 | "description": "request success", 134 | "schema": { 135 | "type": "boolean" 136 | } 137 | } 138 | } 139 | } 140 | }, 141 | "/V1/anonymous7": { 142 | "post": { 143 | "tags": [ 144 | "v1" 145 | ], 146 | "summary": "test16 name", 147 | "description": "test16 description", 148 | "responses": { 149 | "200": { 150 | "description": "request success", 151 | "schema": { 152 | "type": "object", 153 | "additionalProperties": { 154 | "$ref": "#/definitions/testSimpleStruct" 155 | } 156 | } 157 | } 158 | } 159 | } 160 | }, 161 | "/V1/date1": { 162 | "post": { 163 | "tags": [ 164 | "v1" 165 | ], 166 | "summary": "test date 1 name", 167 | "description": "test date 1 description", 168 | "responses": { 169 | "200": { 170 | "description": "request success", 171 | "schema": { 172 | "$ref": "#/definitions/simpleDateTime" 173 | } 174 | } 175 | } 176 | } 177 | }, 178 | "/V1/date2": { 179 | "post": { 180 | "tags": [ 181 | "v1" 182 | ], 183 | "summary": "test date 2 name", 184 | "description": "test date 2 description", 185 | "responses": { 186 | "200": { 187 | "description": "request success", 188 | "schema": { 189 | "$ref": "#/definitions/sliceDateTime" 190 | } 191 | } 192 | } 193 | } 194 | }, 195 | "/V1/date3": { 196 | "post": { 197 | "tags": [ 198 | "v1" 199 | ], 200 | "summary": "test date 3 name", 201 | "description": "test date 3 description", 202 | "responses": { 203 | "200": { 204 | "description": "request success", 205 | "schema": { 206 | "$ref": "#/definitions/mapDateTime" 207 | } 208 | } 209 | } 210 | } 211 | }, 212 | "/V1/date4": { 213 | "post": { 214 | "tags": [ 215 | "v1" 216 | ], 217 | "summary": "test date 4 name", 218 | "description": "test date 4 description", 219 | "responses": { 220 | "200": { 221 | "description": "request success", 222 | "schema": { 223 | "type": "array", 224 | "items": { 225 | "$ref": "#/definitions/mapDateTime" 226 | } 227 | } 228 | } 229 | } 230 | } 231 | }, 232 | "/V1/defaults1": { 233 | "post": { 234 | "tags": [ 235 | "v1" 236 | ], 237 | "summary": "default", 238 | "description": "test defaults", 239 | "responses": { 240 | "200": { 241 | "description": "request success", 242 | "schema": { 243 | "$ref": "#/definitions/testDefaults" 244 | } 245 | } 246 | } 247 | } 248 | }, 249 | "/V1/primitiveTypes1": { 250 | "post": { 251 | "tags": [ 252 | "v1" 253 | ], 254 | "summary": "testPrimitives", 255 | "description": "test Primitives", 256 | "parameters": [ 257 | { 258 | "name": "body", 259 | "in": "body", 260 | "schema": { 261 | "type": "string" 262 | }, 263 | "required": true 264 | } 265 | ], 266 | "responses": { 267 | "200": { 268 | "description": "request success", 269 | "schema": { 270 | "type": "integer", 271 | "format": "int32" 272 | } 273 | } 274 | } 275 | } 276 | }, 277 | "/V1/primitiveTypes2": { 278 | "post": { 279 | "tags": [ 280 | "v1" 281 | ], 282 | "summary": "testPrimitives", 283 | "description": "test Primitives", 284 | "parameters": [ 285 | { 286 | "name": "body", 287 | "in": "body", 288 | "schema": { 289 | "type": "boolean" 290 | }, 291 | "required": true 292 | } 293 | ], 294 | "responses": { 295 | "200": { 296 | "description": "request success", 297 | "schema": { 298 | "type": "number", 299 | "format": "double" 300 | } 301 | } 302 | } 303 | } 304 | }, 305 | "/V1/primitiveTypes3": { 306 | "post": { 307 | "tags": [ 308 | "v1" 309 | ], 310 | "summary": "testPrimitives", 311 | "description": "test Primitives", 312 | "parameters": [ 313 | { 314 | "name": "body", 315 | "in": "body", 316 | "schema": { 317 | "type": "integer", 318 | "format": "int64" 319 | }, 320 | "required": true 321 | } 322 | ], 323 | "responses": { 324 | "200": { 325 | "description": "request success", 326 | "schema": { 327 | "type": "string" 328 | } 329 | } 330 | } 331 | } 332 | }, 333 | "/V1/primitiveTypes4": { 334 | "post": { 335 | "tags": [ 336 | "v1" 337 | ], 338 | "summary": "testPrimitives", 339 | "description": "test Primitives", 340 | "parameters": [ 341 | { 342 | "name": "body", 343 | "in": "body", 344 | "schema": { 345 | "type": "integer", 346 | "format": "int64" 347 | }, 348 | "required": true 349 | } 350 | ], 351 | "responses": { 352 | "200": { 353 | "description": "request success", 354 | "schema": { 355 | "type": "string" 356 | } 357 | } 358 | } 359 | } 360 | }, 361 | "/V1/slice1": { 362 | "post": { 363 | "tags": [ 364 | "v1" 365 | ], 366 | "summary": "test slice 1 name", 367 | "description": "test slice 1 description", 368 | "responses": { 369 | "200": { 370 | "description": "request success", 371 | "schema": { 372 | "type": "array", 373 | "items": { 374 | "$ref": "#/definitions/mapDateTime" 375 | } 376 | } 377 | } 378 | } 379 | } 380 | }, 381 | "/V1/slice2": { 382 | "post": { 383 | "tags": [ 384 | "v1" 385 | ], 386 | "summary": "test slice 2 name", 387 | "description": "test slice 2 description", 388 | "responses": { 389 | "200": { 390 | "description": "request success", 391 | "schema": { 392 | "type": "string" 393 | } 394 | } 395 | } 396 | } 397 | }, 398 | "/V1/test1": { 399 | "post": { 400 | "tags": [ 401 | "v1" 402 | ], 403 | "summary": "test1 name", 404 | "description": "test1 description", 405 | "responses": { 406 | "200": { 407 | "description": "request success", 408 | "schema": { 409 | "$ref": "#/definitions/testSimpleStruct" 410 | } 411 | } 412 | }, 413 | "deprecated": true 414 | } 415 | }, 416 | "/V1/test10": { 417 | "post": { 418 | "tags": [ 419 | "v1" 420 | ], 421 | "summary": "test10 name", 422 | "description": "test10 description", 423 | "parameters": [ 424 | { 425 | "name": "gender", 426 | "in": "query", 427 | "type": "integer", 428 | "format": "int32", 429 | "required": true, 430 | "enum": [ 431 | 0, 432 | 1, 433 | 2, 434 | 3 435 | ], 436 | "x-enum-names": [ 437 | "PreferNotToDisclose", 438 | "Male", 439 | "Female", 440 | "LGBT" 441 | ] 442 | }, 443 | { 444 | "name": "flag", 445 | "in": "query", 446 | "type": "string", 447 | "required": true, 448 | "enum": [ 449 | "Foo", 450 | "Bar" 451 | ], 452 | "x-enum-names": [ 453 | "Foo", 454 | "Bar" 455 | ] 456 | }, 457 | { 458 | "name": "body", 459 | "in": "body", 460 | "schema": { 461 | "$ref": "#/definitions/mixedStruct" 462 | }, 463 | "required": true 464 | } 465 | ], 466 | "responses": { 467 | "200": { 468 | "description": "request success", 469 | "schema": { 470 | "type": "object", 471 | "additionalProperties": { 472 | "$ref": "#/definitions/testSimpleStruct" 473 | } 474 | } 475 | } 476 | }, 477 | "deprecated": true 478 | } 479 | }, 480 | "/V1/test2": { 481 | "post": { 482 | "tags": [ 483 | "v1" 484 | ], 485 | "summary": "test2 name", 486 | "description": "test2 description", 487 | "parameters": [ 488 | { 489 | "name": "simple_string", 490 | "in": "query", 491 | "type": "string", 492 | "required": true 493 | }, 494 | { 495 | "name": "simple_int", 496 | "in": "query", 497 | "type": "integer", 498 | "format": "int32", 499 | "required": true 500 | }, 501 | { 502 | "name": "simple_int32", 503 | "in": "query", 504 | "type": "integer", 505 | "format": "int32", 506 | "required": true 507 | }, 508 | { 509 | "name": "simple_int64", 510 | "in": "query", 511 | "type": "integer", 512 | "format": "int64", 513 | "required": true 514 | }, 515 | { 516 | "name": "simple_uint32", 517 | "in": "query", 518 | "type": "integer", 519 | "format": "int64", 520 | "required": true 521 | }, 522 | { 523 | "name": "simple_uint64", 524 | "in": "query", 525 | "type": "integer", 526 | "format": "int64", 527 | "required": true 528 | }, 529 | { 530 | "name": "simple_float32", 531 | "in": "query", 532 | "type": "number", 533 | "format": "float", 534 | "required": true 535 | }, 536 | { 537 | "name": "simple_float64", 538 | "in": "query", 539 | "type": "number", 540 | "format": "double", 541 | "required": true 542 | }, 543 | { 544 | "name": "simple_bool", 545 | "in": "query", 546 | "type": "boolean", 547 | "required": true 548 | } 549 | ], 550 | "responses": { 551 | "200": { 552 | "description": "request success", 553 | "schema": { 554 | "$ref": "#/definitions/testSimpleSlices" 555 | } 556 | } 557 | }, 558 | "deprecated": true 559 | } 560 | }, 561 | "/V1/test3": { 562 | "post": { 563 | "tags": [ 564 | "v1" 565 | ], 566 | "summary": "test3 name", 567 | "description": "test3 description", 568 | "parameters": [ 569 | { 570 | "name": "body", 571 | "in": "body", 572 | "schema": { 573 | "$ref": "#/definitions/testSimpleSlices" 574 | }, 575 | "required": true 576 | } 577 | ], 578 | "responses": { 579 | "200": { 580 | "description": "request success", 581 | "schema": { 582 | "$ref": "#/definitions/testSimpleMaps" 583 | } 584 | } 585 | }, 586 | "deprecated": true 587 | } 588 | }, 589 | "/V1/test4": { 590 | "post": { 591 | "tags": [ 592 | "v1" 593 | ], 594 | "summary": "test4 name", 595 | "description": "test4 description", 596 | "parameters": [ 597 | { 598 | "name": "body", 599 | "in": "body", 600 | "schema": { 601 | "$ref": "#/definitions/testSimpleMaps" 602 | }, 603 | "required": true 604 | } 605 | ], 606 | "responses": { 607 | "200": { 608 | "description": "request success", 609 | "schema": { 610 | "$ref": "#/definitions/testSimpleMapList" 611 | } 612 | } 613 | }, 614 | "deprecated": true 615 | } 616 | }, 617 | "/V1/test5": { 618 | "post": { 619 | "tags": [ 620 | "v1" 621 | ], 622 | "summary": "test5 name", 623 | "description": "test5 description", 624 | "parameters": [ 625 | { 626 | "name": "body", 627 | "in": "body", 628 | "schema": { 629 | "$ref": "#/definitions/testSimpleMapList" 630 | }, 631 | "required": true 632 | } 633 | ], 634 | "responses": { 635 | "200": { 636 | "description": "request success", 637 | "schema": { 638 | "$ref": "#/definitions/testSubTypes" 639 | } 640 | } 641 | }, 642 | "deprecated": true 643 | } 644 | }, 645 | "/V1/test6": { 646 | "post": { 647 | "tags": [ 648 | "v1" 649 | ], 650 | "summary": "test6 name", 651 | "description": "test6 description", 652 | "parameters": [ 653 | { 654 | "name": "body", 655 | "in": "body", 656 | "schema": { 657 | "$ref": "#/definitions/testSubTypes" 658 | }, 659 | "required": true 660 | } 661 | ], 662 | "responses": { 663 | "200": { 664 | "description": "request success", 665 | "schema": { 666 | "$ref": "#/definitions/testSimpleStruct" 667 | } 668 | } 669 | }, 670 | "deprecated": true 671 | } 672 | }, 673 | "/V1/test7": { 674 | "post": { 675 | "tags": [ 676 | "v1" 677 | ], 678 | "summary": "test7 name", 679 | "description": "test7 description", 680 | "responses": { 681 | "200": { 682 | "description": "request success", 683 | "schema": { 684 | "$ref": "#/definitions/testSimpleSlices" 685 | } 686 | } 687 | }, 688 | "deprecated": true 689 | } 690 | }, 691 | "/V1/test8": { 692 | "post": { 693 | "tags": [ 694 | "v1" 695 | ], 696 | "summary": "test8v1 name", 697 | "description": "test8v1 description", 698 | "parameters": [ 699 | { 700 | "name": "body", 701 | "in": "body", 702 | "schema": { 703 | "$ref": "#/definitions/paramStructMap" 704 | }, 705 | "required": true 706 | } 707 | ], 708 | "responses": { 709 | "200": { 710 | "description": "request success", 711 | "schema": { 712 | "type": "object", 713 | "additionalProperties": { 714 | "$ref": "#/definitions/testSimpleStruct" 715 | } 716 | } 717 | } 718 | }, 719 | "deprecated": true 720 | } 721 | }, 722 | "/V1/test9": { 723 | "post": { 724 | "tags": [ 725 | "v1" 726 | ], 727 | "summary": "test9 name", 728 | "description": "test9 description", 729 | "parameters": [ 730 | { 731 | "name": "fieldQuery", 732 | "in": "query", 733 | "type": "integer", 734 | "format": "int32", 735 | "required": true 736 | }, 737 | { 738 | "name": "body", 739 | "in": "body", 740 | "schema": { 741 | "$ref": "#/definitions/mixedStruct" 742 | }, 743 | "required": true 744 | } 745 | ], 746 | "responses": { 747 | "200": { 748 | "description": "request success", 749 | "schema": { 750 | "type": "object", 751 | "additionalProperties": { 752 | "$ref": "#/definitions/testSimpleStruct" 753 | } 754 | } 755 | } 756 | }, 757 | "deprecated": true 758 | } 759 | }, 760 | "/V1/typeReplacement1": { 761 | "post": { 762 | "tags": [ 763 | "v1" 764 | ], 765 | "summary": "test9 name", 766 | "description": "test9 description", 767 | "parameters": [ 768 | { 769 | "name": "body", 770 | "in": "body", 771 | "schema": { 772 | "$ref": "#/definitions/testSubTypes" 773 | }, 774 | "required": true 775 | } 776 | ], 777 | "responses": { 778 | "200": { 779 | "description": "request success", 780 | "schema": { 781 | "$ref": "#/definitions/testWrapParams" 782 | } 783 | } 784 | } 785 | } 786 | } 787 | }, 788 | "definitions": { 789 | "deepReplacementTag": { 790 | "type": "object", 791 | "properties": { 792 | "test_field_1": { 793 | "type": "number", 794 | "format": "double" 795 | } 796 | } 797 | }, 798 | "mapDateTime": { 799 | "type": "object", 800 | "properties": { 801 | "items": { 802 | "type": "object", 803 | "additionalProperties": { 804 | "$ref": "#/definitions/simpleDateTime" 805 | } 806 | } 807 | } 808 | }, 809 | "mixedStruct": { 810 | "type": "object", 811 | "properties": { 812 | "anonProp": { 813 | "type": "integer", 814 | "format": "int32" 815 | }, 816 | "fieldBody": { 817 | "type": "integer", 818 | "format": "int32" 819 | } 820 | } 821 | }, 822 | "paramStructMap": { 823 | "type": "object" 824 | }, 825 | "simpleDateTime": { 826 | "type": "object", 827 | "properties": { 828 | "time": { 829 | "type": "string", 830 | "format": "date-time" 831 | } 832 | } 833 | }, 834 | "sliceDateTime": { 835 | "type": "object", 836 | "properties": { 837 | "items": { 838 | "type": "array", 839 | "items": { 840 | "$ref": "#/definitions/simpleDateTime" 841 | } 842 | } 843 | } 844 | }, 845 | "testDefaults": { 846 | "type": "object", 847 | "properties": { 848 | "field1": { 849 | "default": 25, 850 | "type": "integer", 851 | "format": "int32" 852 | }, 853 | "field2": { 854 | "default": 25.5, 855 | "type": "number", 856 | "format": "double" 857 | }, 858 | "field3": { 859 | "default": "test", 860 | "type": "string" 861 | }, 862 | "field4": { 863 | "default": true, 864 | "type": "boolean" 865 | }, 866 | "field5": { 867 | "default": [ 868 | 1, 869 | 2, 870 | 3 871 | ], 872 | "type": "array", 873 | "items": { 874 | "type": "integer", 875 | "format": "int32" 876 | } 877 | }, 878 | "field6": { 879 | "default": { 880 | "test": 1 881 | }, 882 | "type": "object", 883 | "additionalProperties": { 884 | "type": "integer", 885 | "format": "int32" 886 | } 887 | }, 888 | "field7": { 889 | "default": 25, 890 | "type": "integer", 891 | "format": "int32" 892 | } 893 | } 894 | }, 895 | "testSimpleMapList": { 896 | "type": "object", 897 | "properties": { 898 | "map_list_bool": { 899 | "type": "array", 900 | "items": { 901 | "type": "object", 902 | "additionalProperties": { 903 | "type": "boolean" 904 | } 905 | } 906 | }, 907 | "map_list_float32": { 908 | "type": "array", 909 | "items": { 910 | "type": "object", 911 | "additionalProperties": { 912 | "type": "number", 913 | "format": "float" 914 | } 915 | } 916 | }, 917 | "map_list_float64": { 918 | "type": "array", 919 | "items": { 920 | "type": "object", 921 | "additionalProperties": { 922 | "type": "number", 923 | "format": "double" 924 | } 925 | } 926 | }, 927 | "map_list_int": { 928 | "type": "array", 929 | "items": { 930 | "type": "object", 931 | "additionalProperties": { 932 | "type": "integer", 933 | "format": "int32" 934 | } 935 | } 936 | }, 937 | "map_list_int32": { 938 | "type": "array", 939 | "items": { 940 | "type": "object", 941 | "additionalProperties": { 942 | "type": "integer", 943 | "format": "int32" 944 | } 945 | } 946 | }, 947 | "map_list_int64": { 948 | "type": "array", 949 | "items": { 950 | "type": "object", 951 | "additionalProperties": { 952 | "type": "integer", 953 | "format": "int64" 954 | } 955 | } 956 | }, 957 | "map_list_string": { 958 | "type": "array", 959 | "items": { 960 | "type": "object", 961 | "additionalProperties": { 962 | "type": "string" 963 | } 964 | } 965 | }, 966 | "map_list_uint32": { 967 | "type": "array", 968 | "items": { 969 | "type": "object", 970 | "additionalProperties": { 971 | "type": "integer", 972 | "format": "int64" 973 | } 974 | } 975 | }, 976 | "map_list_uint64": { 977 | "type": "array", 978 | "items": { 979 | "type": "object", 980 | "additionalProperties": { 981 | "type": "integer", 982 | "format": "int64" 983 | } 984 | } 985 | } 986 | } 987 | }, 988 | "testSimpleMaps": { 989 | "type": "object", 990 | "properties": { 991 | "map_bool": { 992 | "type": "object", 993 | "additionalProperties": { 994 | "type": "boolean" 995 | } 996 | }, 997 | "map_float32": { 998 | "type": "object", 999 | "additionalProperties": { 1000 | "type": "number", 1001 | "format": "float" 1002 | } 1003 | }, 1004 | "map_float64": { 1005 | "type": "object", 1006 | "additionalProperties": { 1007 | "type": "number", 1008 | "format": "double" 1009 | } 1010 | }, 1011 | "map_int": { 1012 | "type": "object", 1013 | "additionalProperties": { 1014 | "type": "integer", 1015 | "format": "int32" 1016 | } 1017 | }, 1018 | "map_int32": { 1019 | "type": "object", 1020 | "additionalProperties": { 1021 | "type": "integer", 1022 | "format": "int32" 1023 | } 1024 | }, 1025 | "map_int64": { 1026 | "type": "object", 1027 | "additionalProperties": { 1028 | "type": "integer", 1029 | "format": "int64" 1030 | } 1031 | }, 1032 | "map_string": { 1033 | "type": "object", 1034 | "additionalProperties": { 1035 | "type": "string" 1036 | } 1037 | }, 1038 | "map_uint32": { 1039 | "type": "object", 1040 | "additionalProperties": { 1041 | "type": "integer", 1042 | "format": "int64" 1043 | } 1044 | }, 1045 | "map_uint64": { 1046 | "type": "object", 1047 | "additionalProperties": { 1048 | "type": "integer", 1049 | "format": "int64" 1050 | } 1051 | } 1052 | } 1053 | }, 1054 | "testSimpleSlices": { 1055 | "type": "object", 1056 | "properties": { 1057 | "list_bool": { 1058 | "type": "array", 1059 | "items": { 1060 | "type": "boolean" 1061 | } 1062 | }, 1063 | "list_float32": { 1064 | "type": "array", 1065 | "items": { 1066 | "type": "number", 1067 | "format": "float" 1068 | } 1069 | }, 1070 | "list_float64": { 1071 | "type": "array", 1072 | "items": { 1073 | "type": "number", 1074 | "format": "double" 1075 | } 1076 | }, 1077 | "list_int": { 1078 | "type": "array", 1079 | "items": { 1080 | "type": "integer", 1081 | "format": "int32" 1082 | } 1083 | }, 1084 | "list_int32": { 1085 | "type": "array", 1086 | "items": { 1087 | "type": "integer", 1088 | "format": "int32" 1089 | } 1090 | }, 1091 | "list_int64": { 1092 | "type": "array", 1093 | "items": { 1094 | "type": "integer", 1095 | "format": "int64" 1096 | } 1097 | }, 1098 | "list_string": { 1099 | "type": "array", 1100 | "items": { 1101 | "type": "string" 1102 | } 1103 | }, 1104 | "list_uint32": { 1105 | "type": "array", 1106 | "items": { 1107 | "type": "integer", 1108 | "format": "int64" 1109 | } 1110 | }, 1111 | "list_uint64": { 1112 | "type": "array", 1113 | "items": { 1114 | "type": "integer", 1115 | "format": "int64" 1116 | } 1117 | } 1118 | } 1119 | }, 1120 | "testSimpleStruct": { 1121 | "type": "object", 1122 | "properties": { 1123 | "simple_bool": { 1124 | "type": "boolean" 1125 | }, 1126 | "simple_float32": { 1127 | "type": "number", 1128 | "format": "float" 1129 | }, 1130 | "simple_float64": { 1131 | "type": "number", 1132 | "format": "double" 1133 | }, 1134 | "simple_int": { 1135 | "type": "integer", 1136 | "format": "int32" 1137 | }, 1138 | "simple_int32": { 1139 | "type": "integer", 1140 | "format": "int32" 1141 | }, 1142 | "simple_int64": { 1143 | "type": "integer", 1144 | "format": "int64" 1145 | }, 1146 | "simple_string": { 1147 | "type": "string" 1148 | }, 1149 | "simple_uint32": { 1150 | "type": "integer", 1151 | "format": "int64" 1152 | }, 1153 | "simple_uint64": { 1154 | "type": "integer", 1155 | "format": "int64" 1156 | } 1157 | } 1158 | }, 1159 | "testSubTypes": { 1160 | "type": "object", 1161 | "properties": { 1162 | "test_simple_map_list": { 1163 | "$ref": "#/definitions/testSimpleMapList" 1164 | }, 1165 | "test_simple_maps": { 1166 | "$ref": "#/definitions/testSimpleMaps" 1167 | }, 1168 | "test_simple_slices": { 1169 | "$ref": "#/definitions/testSimpleSlices" 1170 | }, 1171 | "test_simple_struct": { 1172 | "$ref": "#/definitions/testSimpleStruct" 1173 | } 1174 | } 1175 | }, 1176 | "testWrapParams": { 1177 | "type": "object", 1178 | "properties": { 1179 | "deep_replacement": { 1180 | "$ref": "#/definitions/deepReplacementTag" 1181 | }, 1182 | "should_be_sting": { 1183 | "type": "string" 1184 | }, 1185 | "simple_test_replacement": { 1186 | "$ref": "#/definitions/simpleTestReplacement" 1187 | } 1188 | } 1189 | } 1190 | }, 1191 | "x-service-type": "json-rpc" 1192 | } -------------------------------------------------------------------------------- /testdata/test_REST.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "swgen title", 5 | "description": "swgen description", 6 | "termsOfService": "term", 7 | "contact": { 8 | "name": "Dylan Noblitt", 9 | "url": "http://example.com", 10 | "email": "dylan.noblitt@example.com" 11 | }, 12 | "license": { 13 | "name": "BEER-WARE", 14 | "url": "https://fedoraproject.org/wiki/Licensing/Beerware" 15 | }, 16 | "version": "2.0" 17 | }, 18 | "host": "localhost", 19 | "basePath": "/", 20 | "schemes": [ 21 | "http", 22 | "https" 23 | ], 24 | "paths": { 25 | "/V1/IDefinition1": { 26 | "post": { 27 | "tags": [ 28 | "v1" 29 | ], 30 | "summary": "test IDefinition1 name", 31 | "description": "test IDefinition1 description", 32 | "parameters": [ 33 | { 34 | "name": "body", 35 | "in": "body", 36 | "schema": { 37 | "$ref": "#/definitions/definitionExample" 38 | }, 39 | "required": true 40 | } 41 | ], 42 | "responses": { 43 | "200": { 44 | "description": "request success", 45 | "schema": { 46 | "$ref": "#/definitions/definitionExample" 47 | } 48 | } 49 | }, 50 | "x-request-go-type": "github.com/lazada/swgen.definitionExample" 51 | } 52 | }, 53 | "/V1/anonymous1": { 54 | "post": { 55 | "tags": [ 56 | "v1" 57 | ], 58 | "summary": "test10 name", 59 | "description": "test10 description", 60 | "parameters": [ 61 | { 62 | "name": "body", 63 | "in": "body", 64 | "schema": { 65 | "$ref": "#/definitions/testSimpleStruct" 66 | }, 67 | "required": true 68 | } 69 | ], 70 | "responses": { 71 | "200": { 72 | "description": "request success", 73 | "schema": { 74 | "type": "object", 75 | "additionalProperties": { 76 | "type": "integer", 77 | "format": "int64", 78 | "x-go-type": "int64" 79 | }, 80 | "x-go-type": "map[string]int64" 81 | } 82 | } 83 | }, 84 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 85 | } 86 | }, 87 | "/V1/anonymous2": { 88 | "post": { 89 | "tags": [ 90 | "v1" 91 | ], 92 | "summary": "test11 name", 93 | "description": "test11 description", 94 | "parameters": [ 95 | { 96 | "name": "body", 97 | "in": "body", 98 | "schema": { 99 | "$ref": "#/definitions/testSimpleStruct" 100 | }, 101 | "required": true 102 | } 103 | ], 104 | "responses": { 105 | "200": { 106 | "description": "request success", 107 | "schema": { 108 | "type": "object", 109 | "additionalProperties": { 110 | "type": "string", 111 | "x-go-type": "string" 112 | }, 113 | "x-go-type": "map[float64]string" 114 | } 115 | } 116 | }, 117 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 118 | } 119 | }, 120 | "/V1/anonymous3": { 121 | "post": { 122 | "tags": [ 123 | "v1" 124 | ], 125 | "summary": "test12 name", 126 | "description": "test12 description", 127 | "parameters": [ 128 | { 129 | "name": "body", 130 | "in": "body", 131 | "schema": { 132 | "$ref": "#/definitions/testSimpleStruct" 133 | }, 134 | "required": true 135 | } 136 | ], 137 | "responses": { 138 | "200": { 139 | "description": "request success", 140 | "schema": { 141 | "type": "array", 142 | "items": { 143 | "type": "string", 144 | "x-go-type": "string" 145 | }, 146 | "x-go-type": "[]string" 147 | } 148 | } 149 | }, 150 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 151 | } 152 | }, 153 | "/V1/anonymous4": { 154 | "post": { 155 | "tags": [ 156 | "v1" 157 | ], 158 | "summary": "test13 name", 159 | "description": "test13 description", 160 | "parameters": [ 161 | { 162 | "name": "body", 163 | "in": "body", 164 | "schema": { 165 | "$ref": "#/definitions/testSimpleStruct" 166 | }, 167 | "required": true 168 | } 169 | ], 170 | "responses": { 171 | "200": { 172 | "description": "request success", 173 | "schema": { 174 | "type": "array", 175 | "items": { 176 | "type": "integer", 177 | "format": "int32", 178 | "x-go-type": "int" 179 | }, 180 | "x-go-type": "[]int" 181 | } 182 | } 183 | }, 184 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 185 | } 186 | }, 187 | "/V1/anonymous5": { 188 | "post": { 189 | "tags": [ 190 | "v1" 191 | ], 192 | "summary": "test14 name", 193 | "description": "test14 description", 194 | "parameters": [ 195 | { 196 | "name": "body", 197 | "in": "body", 198 | "schema": { 199 | "$ref": "#/definitions/testSimpleStruct" 200 | }, 201 | "required": true 202 | } 203 | ], 204 | "responses": { 205 | "200": { 206 | "description": "request success", 207 | "schema": { 208 | "type": "string", 209 | "x-go-type": "string" 210 | } 211 | } 212 | }, 213 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 214 | } 215 | }, 216 | "/V1/anonymous6": { 217 | "post": { 218 | "tags": [ 219 | "v1" 220 | ], 221 | "summary": "test15 name", 222 | "description": "test15 description", 223 | "parameters": [ 224 | { 225 | "name": "body", 226 | "in": "body", 227 | "schema": { 228 | "$ref": "#/definitions/testSimpleStruct" 229 | }, 230 | "required": true 231 | } 232 | ], 233 | "responses": { 234 | "200": { 235 | "description": "request success", 236 | "schema": { 237 | "type": "boolean", 238 | "x-go-type": "bool" 239 | } 240 | } 241 | }, 242 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 243 | } 244 | }, 245 | "/V1/anonymous7": { 246 | "post": { 247 | "tags": [ 248 | "v1" 249 | ], 250 | "summary": "test16 name", 251 | "description": "test16 description", 252 | "parameters": [ 253 | { 254 | "name": "body", 255 | "in": "body", 256 | "schema": { 257 | "$ref": "#/definitions/testSimpleStruct" 258 | }, 259 | "required": true 260 | } 261 | ], 262 | "responses": { 263 | "200": { 264 | "description": "request success", 265 | "schema": { 266 | "type": "object", 267 | "additionalProperties": { 268 | "$ref": "#/definitions/testSimpleStruct" 269 | }, 270 | "x-go-type": "map[string]github.com/lazada/swgen.testSimpleStruct" 271 | } 272 | } 273 | }, 274 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 275 | } 276 | }, 277 | "/V1/combine": { 278 | "get": { 279 | "tags": [ 280 | "v1" 281 | ], 282 | "summary": "test1 name", 283 | "description": "test1 description", 284 | "responses": { 285 | "200": { 286 | "description": "request success", 287 | "schema": { 288 | "$ref": "#/definitions/testSimpleStruct" 289 | } 290 | } 291 | }, 292 | "deprecated": true 293 | }, 294 | "put": { 295 | "tags": [ 296 | "v1" 297 | ], 298 | "summary": "test3 name", 299 | "description": "test3 description", 300 | "parameters": [ 301 | { 302 | "name": "body", 303 | "in": "body", 304 | "schema": { 305 | "$ref": "#/definitions/testSimpleSlices" 306 | }, 307 | "required": true 308 | } 309 | ], 310 | "responses": { 311 | "200": { 312 | "description": "request success", 313 | "schema": { 314 | "$ref": "#/definitions/testSimpleMaps" 315 | } 316 | } 317 | }, 318 | "deprecated": true, 319 | "x-request-go-type": "github.com/lazada/swgen.testSimpleSlices" 320 | }, 321 | "post": { 322 | "tags": [ 323 | "v1" 324 | ], 325 | "summary": "test4 name", 326 | "description": "test4 description", 327 | "parameters": [ 328 | { 329 | "name": "body", 330 | "in": "body", 331 | "schema": { 332 | "$ref": "#/definitions/testSimpleMaps" 333 | }, 334 | "required": true 335 | } 336 | ], 337 | "responses": { 338 | "200": { 339 | "description": "request success", 340 | "schema": { 341 | "$ref": "#/definitions/testSimpleMapList" 342 | } 343 | } 344 | }, 345 | "deprecated": true, 346 | "x-request-go-type": "github.com/lazada/swgen.testSimpleMaps" 347 | }, 348 | "delete": { 349 | "tags": [ 350 | "v1" 351 | ], 352 | "summary": "test5 name", 353 | "description": "test5 description", 354 | "parameters": [ 355 | { 356 | "name": "body", 357 | "in": "body", 358 | "schema": { 359 | "$ref": "#/definitions/testSimpleMapList" 360 | }, 361 | "required": true 362 | } 363 | ], 364 | "responses": { 365 | "200": { 366 | "description": "request success", 367 | "schema": { 368 | "$ref": "#/definitions/testSubTypes" 369 | } 370 | } 371 | }, 372 | "deprecated": true, 373 | "x-request-go-type": "github.com/lazada/swgen.testSimpleMapList" 374 | }, 375 | "options": { 376 | "tags": [ 377 | "v1" 378 | ], 379 | "summary": "test7 name", 380 | "description": "test7 description", 381 | "parameters": [ 382 | { 383 | "name": "body", 384 | "in": "body", 385 | "schema": { 386 | "$ref": "#/definitions/testSubTypes" 387 | }, 388 | "required": true 389 | } 390 | ], 391 | "responses": { 392 | "200": { 393 | "description": "request success", 394 | "schema": { 395 | "$ref": "#/definitions/testSimpleStruct" 396 | } 397 | } 398 | }, 399 | "deprecated": true, 400 | "x-request-go-type": "github.com/lazada/swgen.testSubTypes" 401 | }, 402 | "patch": { 403 | "tags": [ 404 | "v1" 405 | ], 406 | "summary": "test6 name", 407 | "description": "test6 description", 408 | "parameters": [ 409 | { 410 | "name": "body", 411 | "in": "body", 412 | "schema": { 413 | "$ref": "#/definitions/testSubTypes" 414 | }, 415 | "required": true 416 | } 417 | ], 418 | "responses": { 419 | "200": { 420 | "description": "request success", 421 | "schema": { 422 | "$ref": "#/definitions/testSimpleStruct" 423 | } 424 | } 425 | }, 426 | "deprecated": true, 427 | "x-request-go-type": "github.com/lazada/swgen.testSubTypes" 428 | } 429 | }, 430 | "/V1/date1": { 431 | "post": { 432 | "tags": [ 433 | "v1" 434 | ], 435 | "summary": "test date 1 name", 436 | "description": "test date 1 description", 437 | "parameters": [ 438 | { 439 | "name": "body", 440 | "in": "body", 441 | "schema": { 442 | "$ref": "#/definitions/testSimpleStruct" 443 | }, 444 | "required": true 445 | } 446 | ], 447 | "responses": { 448 | "200": { 449 | "description": "request success", 450 | "schema": { 451 | "$ref": "#/definitions/simpleDateTime" 452 | } 453 | } 454 | }, 455 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 456 | } 457 | }, 458 | "/V1/date2": { 459 | "post": { 460 | "tags": [ 461 | "v1" 462 | ], 463 | "summary": "test date 2 name", 464 | "description": "test date 2 description", 465 | "parameters": [ 466 | { 467 | "name": "body", 468 | "in": "body", 469 | "schema": { 470 | "$ref": "#/definitions/testSimpleStruct" 471 | }, 472 | "required": true 473 | } 474 | ], 475 | "responses": { 476 | "200": { 477 | "description": "request success", 478 | "schema": { 479 | "$ref": "#/definitions/sliceDateTime" 480 | } 481 | } 482 | }, 483 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 484 | } 485 | }, 486 | "/V1/date3": { 487 | "post": { 488 | "tags": [ 489 | "v1" 490 | ], 491 | "summary": "test date 3 name", 492 | "description": "test date 3 description", 493 | "parameters": [ 494 | { 495 | "name": "body", 496 | "in": "body", 497 | "schema": { 498 | "$ref": "#/definitions/testSimpleStruct" 499 | }, 500 | "required": true 501 | } 502 | ], 503 | "responses": { 504 | "200": { 505 | "description": "request success", 506 | "schema": { 507 | "$ref": "#/definitions/mapDateTime" 508 | } 509 | } 510 | }, 511 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 512 | } 513 | }, 514 | "/V1/date4": { 515 | "post": { 516 | "tags": [ 517 | "v1" 518 | ], 519 | "summary": "test date 4 name", 520 | "description": "test date 4 description", 521 | "parameters": [ 522 | { 523 | "name": "body", 524 | "in": "body", 525 | "schema": { 526 | "$ref": "#/definitions/testSimpleStruct" 527 | }, 528 | "required": true 529 | } 530 | ], 531 | "responses": { 532 | "200": { 533 | "description": "request success", 534 | "schema": { 535 | "type": "array", 536 | "items": { 537 | "$ref": "#/definitions/mapDateTime" 538 | }, 539 | "x-go-type": "[]github.com/lazada/swgen.mapDateTime" 540 | } 541 | } 542 | }, 543 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 544 | } 545 | }, 546 | "/V1/defaeults1": { 547 | "get": { 548 | "tags": [ 549 | "v1" 550 | ], 551 | "summary": "default", 552 | "description": "test defaults", 553 | "responses": { 554 | "200": { 555 | "description": "request success", 556 | "schema": { 557 | "$ref": "#/definitions/testDefaults" 558 | } 559 | } 560 | } 561 | } 562 | }, 563 | "/V1/empty": { 564 | "post": { 565 | "tags": [ 566 | "v1" 567 | ], 568 | "summary": "test empty struct", 569 | "description": "test empty struct", 570 | "responses": { 571 | "200": { 572 | "description": "request success", 573 | "schema": { 574 | "$ref": "#/definitions/testEmptyStruct" 575 | } 576 | } 577 | }, 578 | "x-request-go-type": "github.com/lazada/swgen.testEmptyStruct" 579 | } 580 | }, 581 | "/V1/nullTypes": { 582 | "get": { 583 | "tags": [ 584 | "v1" 585 | ], 586 | "summary": "test nulltypes", 587 | "description": "test nulltypes", 588 | "parameters": [ 589 | { 590 | "name": "body", 591 | "in": "body", 592 | "schema": { 593 | "$ref": "#/definitions/NullTypes" 594 | }, 595 | "required": true 596 | } 597 | ], 598 | "responses": { 599 | "200": { 600 | "description": "request success", 601 | "schema": { 602 | "$ref": "#/definitions/NullTypes" 603 | } 604 | } 605 | }, 606 | "x-request-go-type": "github.com/lazada/swgen.NullTypes" 607 | } 608 | }, 609 | "/V1/pathParams/{category}/{id}": { 610 | "get": { 611 | "tags": [ 612 | "V1" 613 | ], 614 | "summary": "test8 name", 615 | "description": "test8 description", 616 | "parameters": [ 617 | { 618 | "name": "id", 619 | "in": "path", 620 | "type": "integer", 621 | "format": "int64", 622 | "x-go-name": "ID", 623 | "x-go-type": "uint64" 624 | }, 625 | { 626 | "name": "category", 627 | "in": "path", 628 | "type": "string", 629 | "required": true, 630 | "x-go-name": "Cat", 631 | "x-go-type": "string" 632 | } 633 | ], 634 | "responses": { 635 | "200": { 636 | "description": "request success", 637 | "schema": { 638 | "$ref": "#/definitions/testSimpleStruct" 639 | } 640 | } 641 | }, 642 | "x-request-go-type": "github.com/lazada/swgen.testPathParam" 643 | } 644 | }, 645 | "/V1/primitiveTypes1": { 646 | "post": { 647 | "tags": [ 648 | "v1" 649 | ], 650 | "summary": "testPrimitives", 651 | "description": "test Primitives", 652 | "parameters": [ 653 | { 654 | "name": "body", 655 | "in": "body", 656 | "schema": { 657 | "type": "string", 658 | "x-go-type": "string" 659 | }, 660 | "required": true 661 | } 662 | ], 663 | "responses": { 664 | "200": { 665 | "description": "request success", 666 | "schema": { 667 | "type": "integer", 668 | "format": "int32", 669 | "x-go-type": "int" 670 | } 671 | } 672 | }, 673 | "x-request-go-type": "string" 674 | } 675 | }, 676 | "/V1/primitiveTypes2": { 677 | "post": { 678 | "tags": [ 679 | "v1" 680 | ], 681 | "summary": "testPrimitives", 682 | "description": "test Primitives", 683 | "parameters": [ 684 | { 685 | "name": "body", 686 | "in": "body", 687 | "schema": { 688 | "type": "boolean", 689 | "x-go-type": "bool" 690 | }, 691 | "required": true 692 | } 693 | ], 694 | "responses": { 695 | "200": { 696 | "description": "request success", 697 | "schema": { 698 | "type": "number", 699 | "format": "double", 700 | "x-go-type": "float64" 701 | } 702 | } 703 | }, 704 | "x-request-go-type": "bool" 705 | } 706 | }, 707 | "/V1/primitiveTypes3": { 708 | "post": { 709 | "tags": [ 710 | "v1" 711 | ], 712 | "summary": "testPrimitives", 713 | "description": "test Primitives", 714 | "parameters": [ 715 | { 716 | "name": "body", 717 | "in": "body", 718 | "schema": { 719 | "type": "integer", 720 | "format": "int64", 721 | "x-go-type": "int64" 722 | }, 723 | "required": true 724 | } 725 | ], 726 | "responses": { 727 | "200": { 728 | "description": "request success", 729 | "schema": { 730 | "type": "string", 731 | "x-go-type": "string" 732 | } 733 | } 734 | }, 735 | "x-request-go-type": "int64" 736 | } 737 | }, 738 | "/V1/primitiveTypes4": { 739 | "post": { 740 | "tags": [ 741 | "v1" 742 | ], 743 | "summary": "testPrimitives", 744 | "description": "test Primitives", 745 | "parameters": [ 746 | { 747 | "name": "body", 748 | "in": "body", 749 | "schema": { 750 | "type": "integer", 751 | "format": "int64", 752 | "x-go-type": "int64" 753 | }, 754 | "required": true 755 | } 756 | ], 757 | "responses": { 758 | "200": { 759 | "description": "request success", 760 | "schema": { 761 | "type": "string", 762 | "x-go-type": "string" 763 | } 764 | } 765 | }, 766 | "x-request-go-type": "int64" 767 | } 768 | }, 769 | "/V1/slice1": { 770 | "post": { 771 | "tags": [ 772 | "v1" 773 | ], 774 | "summary": "test slice 1 name", 775 | "description": "test slice 1 description", 776 | "parameters": [ 777 | { 778 | "name": "body", 779 | "in": "body", 780 | "schema": { 781 | "$ref": "#/definitions/testSimpleStruct" 782 | }, 783 | "required": true 784 | } 785 | ], 786 | "responses": { 787 | "200": { 788 | "description": "request success", 789 | "schema": { 790 | "type": "array", 791 | "items": { 792 | "$ref": "#/definitions/mapDateTime" 793 | }, 794 | "x-go-type": "[]github.com/lazada/swgen.mapDateTime" 795 | } 796 | } 797 | }, 798 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 799 | } 800 | }, 801 | "/V1/slice2": { 802 | "post": { 803 | "tags": [ 804 | "v1" 805 | ], 806 | "summary": "test slice 2 name", 807 | "description": "test slice 2 description", 808 | "parameters": [ 809 | { 810 | "name": "body", 811 | "in": "body", 812 | "schema": { 813 | "$ref": "#/definitions/testSimpleStruct" 814 | }, 815 | "required": true 816 | } 817 | ], 818 | "responses": { 819 | "200": { 820 | "description": "request success", 821 | "schema": { 822 | "type": "number", 823 | "format": "double", 824 | "x-go-type": "float64" 825 | } 826 | } 827 | }, 828 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 829 | } 830 | }, 831 | "/V1/struct-collision": { 832 | "post": { 833 | "tags": [ 834 | "v1" 835 | ], 836 | "summary": "test struct name collision", 837 | "description": "test struct name collision", 838 | "parameters": [ 839 | { 840 | "name": "body", 841 | "in": "body", 842 | "schema": { 843 | "$ref": "#/definitions/TestSampleStruct" 844 | }, 845 | "required": true 846 | } 847 | ], 848 | "responses": { 849 | "200": { 850 | "description": "request success", 851 | "schema": { 852 | "$ref": "#/definitions/TestSampleStruct" 853 | } 854 | } 855 | }, 856 | "x-request-go-type": "github.com/lazada/swgen.TestSampleStruct" 857 | } 858 | }, 859 | "/V1/test1": { 860 | "get": { 861 | "tags": [ 862 | "v1" 863 | ], 864 | "summary": "test1 name", 865 | "description": "test1 description", 866 | "responses": { 867 | "200": { 868 | "description": "request success", 869 | "schema": { 870 | "$ref": "#/definitions/testSimpleStruct" 871 | } 872 | } 873 | } 874 | } 875 | }, 876 | "/V1/test2": { 877 | "get": { 878 | "tags": [ 879 | "v1" 880 | ], 881 | "summary": "test2 name", 882 | "description": "test2 description", 883 | "parameters": [ 884 | { 885 | "name": "simple_string", 886 | "in": "query", 887 | "type": "string", 888 | "required": true, 889 | "x-go-name": "SimpleString", 890 | "x-go-type": "string" 891 | }, 892 | { 893 | "name": "simple_int", 894 | "in": "query", 895 | "type": "integer", 896 | "format": "int32", 897 | "required": true, 898 | "x-go-name": "SimpleInt", 899 | "x-go-type": "int" 900 | }, 901 | { 902 | "name": "simple_int32", 903 | "in": "query", 904 | "type": "integer", 905 | "format": "int32", 906 | "required": true, 907 | "x-go-name": "SimpleInt32", 908 | "x-go-type": "int32" 909 | }, 910 | { 911 | "name": "simple_int64", 912 | "in": "query", 913 | "type": "integer", 914 | "format": "int64", 915 | "required": true, 916 | "x-go-name": "SimpleInt64", 917 | "x-go-type": "int64" 918 | }, 919 | { 920 | "name": "simple_uint32", 921 | "in": "query", 922 | "type": "integer", 923 | "format": "int64", 924 | "required": true, 925 | "x-go-name": "SimpleUInt32", 926 | "x-go-type": "uint32" 927 | }, 928 | { 929 | "name": "simple_uint64", 930 | "in": "query", 931 | "type": "integer", 932 | "format": "int64", 933 | "required": true, 934 | "x-go-name": "SimpleUInt64", 935 | "x-go-type": "uint64" 936 | }, 937 | { 938 | "name": "simple_float32", 939 | "in": "query", 940 | "type": "number", 941 | "format": "float", 942 | "required": true, 943 | "x-go-name": "SimpleFloat32", 944 | "x-go-type": "float32" 945 | }, 946 | { 947 | "name": "simple_float64", 948 | "in": "query", 949 | "type": "number", 950 | "format": "double", 951 | "required": true, 952 | "x-go-name": "SimpleFloat64", 953 | "x-go-type": "float64" 954 | }, 955 | { 956 | "name": "simple_bool", 957 | "in": "query", 958 | "type": "boolean", 959 | "required": true, 960 | "x-go-name": "SimpleBool", 961 | "x-go-type": "bool" 962 | } 963 | ], 964 | "responses": { 965 | "200": { 966 | "description": "request success", 967 | "schema": { 968 | "$ref": "#/definitions/testSimpleSlices" 969 | } 970 | } 971 | }, 972 | "x-request-go-type": "github.com/lazada/swgen.testSimpleStruct" 973 | } 974 | }, 975 | "/V1/test3": { 976 | "put": { 977 | "tags": [ 978 | "v1" 979 | ], 980 | "summary": "test3 name", 981 | "description": "test3 description", 982 | "parameters": [ 983 | { 984 | "name": "body", 985 | "in": "body", 986 | "schema": { 987 | "$ref": "#/definitions/testSimpleSlices" 988 | }, 989 | "required": true 990 | } 991 | ], 992 | "responses": { 993 | "200": { 994 | "description": "request success", 995 | "schema": { 996 | "$ref": "#/definitions/testSimpleMaps" 997 | } 998 | } 999 | }, 1000 | "x-request-go-type": "github.com/lazada/swgen.testSimpleSlices" 1001 | } 1002 | }, 1003 | "/V1/test4": { 1004 | "post": { 1005 | "tags": [ 1006 | "v1" 1007 | ], 1008 | "summary": "test4 name", 1009 | "description": "test4 description", 1010 | "parameters": [ 1011 | { 1012 | "name": "body", 1013 | "in": "body", 1014 | "schema": { 1015 | "$ref": "#/definitions/testSimpleMaps" 1016 | }, 1017 | "required": true 1018 | } 1019 | ], 1020 | "responses": { 1021 | "200": { 1022 | "description": "request success", 1023 | "schema": { 1024 | "$ref": "#/definitions/testSimpleMapList" 1025 | } 1026 | } 1027 | }, 1028 | "x-request-go-type": "github.com/lazada/swgen.testSimpleMaps" 1029 | } 1030 | }, 1031 | "/V1/test5": { 1032 | "delete": { 1033 | "tags": [ 1034 | "v1" 1035 | ], 1036 | "summary": "test5 name", 1037 | "description": "test5 description", 1038 | "parameters": [ 1039 | { 1040 | "name": "body", 1041 | "in": "body", 1042 | "schema": { 1043 | "$ref": "#/definitions/testSimpleMapList" 1044 | }, 1045 | "required": true 1046 | } 1047 | ], 1048 | "responses": { 1049 | "200": { 1050 | "description": "request success", 1051 | "schema": { 1052 | "$ref": "#/definitions/testSubTypes" 1053 | } 1054 | } 1055 | }, 1056 | "x-request-go-type": "github.com/lazada/swgen.testSimpleMapList" 1057 | } 1058 | }, 1059 | "/V1/test6": { 1060 | "patch": { 1061 | "tags": [ 1062 | "v1" 1063 | ], 1064 | "summary": "test6 name", 1065 | "description": "test6 description", 1066 | "parameters": [ 1067 | { 1068 | "name": "body", 1069 | "in": "body", 1070 | "schema": { 1071 | "$ref": "#/definitions/testSubTypes" 1072 | }, 1073 | "required": true 1074 | } 1075 | ], 1076 | "responses": { 1077 | "200": { 1078 | "description": "request success", 1079 | "schema": { 1080 | "$ref": "#/definitions/testSimpleStruct" 1081 | } 1082 | } 1083 | }, 1084 | "x-request-go-type": "github.com/lazada/swgen.testSubTypes" 1085 | } 1086 | }, 1087 | "/V1/test7": { 1088 | "options": { 1089 | "tags": [ 1090 | "v1" 1091 | ], 1092 | "summary": "test7 name", 1093 | "description": "test7 description", 1094 | "responses": { 1095 | "200": { 1096 | "description": "request success", 1097 | "schema": { 1098 | "$ref": "#/definitions/testSimpleSlices" 1099 | } 1100 | } 1101 | } 1102 | } 1103 | }, 1104 | "/V1/test8": { 1105 | "get": { 1106 | "tags": [ 1107 | "v1" 1108 | ], 1109 | "summary": "test8v1 name", 1110 | "description": "test8v1 description", 1111 | "parameters": [ 1112 | { 1113 | "name": "field1", 1114 | "in": "query", 1115 | "type": "integer", 1116 | "format": "int32", 1117 | "required": true, 1118 | "x-go-name": "Field1", 1119 | "x-go-type": "int" 1120 | }, 1121 | { 1122 | "name": "field2", 1123 | "in": "query", 1124 | "type": "string", 1125 | "required": true, 1126 | "x-go-name": "Field2", 1127 | "x-go-type": "string" 1128 | }, 1129 | { 1130 | "name": "field3", 1131 | "in": "query", 1132 | "type": "string", 1133 | "required": true, 1134 | "x-go-name": "Field3", 1135 | "x-go-type": "github.com/lazada/swgen.simpleTestReplacement" 1136 | } 1137 | ], 1138 | "responses": { 1139 | "200": { 1140 | "description": "request success", 1141 | "schema": { 1142 | "type": "object", 1143 | "additionalProperties": { 1144 | "$ref": "#/definitions/testSimpleStruct" 1145 | }, 1146 | "x-go-type": "map[string]github.com/lazada/swgen.testSimpleStruct" 1147 | } 1148 | } 1149 | }, 1150 | "x-request-go-type": "github.com/lazada/swgen.paramStructMap" 1151 | } 1152 | }, 1153 | "/V1/test9": { 1154 | "get": { 1155 | "tags": [ 1156 | "v1" 1157 | ], 1158 | "summary": "test9 name", 1159 | "description": "test9 description", 1160 | "parameters": [ 1161 | { 1162 | "name": "fieldQuery", 1163 | "in": "query", 1164 | "type": "integer", 1165 | "format": "int32", 1166 | "required": true, 1167 | "x-go-name": "FieldQuery", 1168 | "x-go-type": "int" 1169 | }, 1170 | { 1171 | "name": "body", 1172 | "in": "body", 1173 | "schema": { 1174 | "$ref": "#/definitions/mixedStruct" 1175 | }, 1176 | "required": true 1177 | } 1178 | ], 1179 | "responses": { 1180 | "200": { 1181 | "description": "request success", 1182 | "schema": { 1183 | "type": "object", 1184 | "additionalProperties": { 1185 | "$ref": "#/definitions/testSimpleStruct" 1186 | }, 1187 | "x-go-type": "map[string]github.com/lazada/swgen.testSimpleStruct" 1188 | } 1189 | } 1190 | }, 1191 | "x-request-go-type": "github.com/lazada/swgen.mixedStruct" 1192 | } 1193 | }, 1194 | "/V1/type-map": { 1195 | "post": { 1196 | "tags": [ 1197 | "v1" 1198 | ], 1199 | "summary": "test type mapping", 1200 | "description": "test type mapping", 1201 | "responses": { 1202 | "200": { 1203 | "description": "request success", 1204 | "schema": { 1205 | "$ref": "#/definitions/typeMapHolder" 1206 | } 1207 | } 1208 | } 1209 | } 1210 | }, 1211 | "/V1/typeReplacement1": { 1212 | "post": { 1213 | "tags": [ 1214 | "v1" 1215 | ], 1216 | "summary": "test9 name", 1217 | "description": "test9 description", 1218 | "parameters": [ 1219 | { 1220 | "name": "body", 1221 | "in": "body", 1222 | "schema": { 1223 | "$ref": "#/definitions/testSubTypes" 1224 | }, 1225 | "required": true 1226 | } 1227 | ], 1228 | "responses": { 1229 | "200": { 1230 | "description": "request success", 1231 | "schema": { 1232 | "$ref": "#/definitions/testWrapParams" 1233 | } 1234 | } 1235 | }, 1236 | "x-request-go-type": "github.com/lazada/swgen.testSubTypes" 1237 | } 1238 | }, 1239 | "/V1/unknown": { 1240 | "post": { 1241 | "tags": [ 1242 | "v1" 1243 | ], 1244 | "summary": "test unknown types", 1245 | "description": "test unknown types", 1246 | "parameters": [ 1247 | { 1248 | "name": "body", 1249 | "in": "body", 1250 | "schema": { 1251 | "$ref": "#/definitions/Unknown" 1252 | }, 1253 | "required": true 1254 | } 1255 | ], 1256 | "responses": { 1257 | "200": { 1258 | "description": "request success", 1259 | "schema": { 1260 | "$ref": "#/definitions/Unknown" 1261 | } 1262 | } 1263 | }, 1264 | "x-request-go-type": "github.com/lazada/swgen.Unknown" 1265 | } 1266 | }, 1267 | "/V2/struct-collision": { 1268 | "post": { 1269 | "tags": [ 1270 | "v2" 1271 | ], 1272 | "summary": "test struct name collision", 1273 | "description": "test struct name collision", 1274 | "parameters": [ 1275 | { 1276 | "name": "body", 1277 | "in": "body", 1278 | "schema": { 1279 | "$ref": "#/definitions/TestSampleStructType2" 1280 | }, 1281 | "required": true 1282 | } 1283 | ], 1284 | "responses": { 1285 | "200": { 1286 | "description": "request success", 1287 | "schema": { 1288 | "$ref": "#/definitions/TestSampleStructType2" 1289 | } 1290 | } 1291 | }, 1292 | "x-request-go-type": "github.com/lazada/swgen/sample.TestSampleStruct" 1293 | } 1294 | } 1295 | }, 1296 | "definitions": { 1297 | "NullBool": { 1298 | "type": "boolean", 1299 | "x-go-type": "github.com/lazada/swgen.NullBool" 1300 | }, 1301 | "NullDate": { 1302 | "type": "string", 1303 | "format": "date", 1304 | "x-go-type": "github.com/lazada/swgen.NullDate" 1305 | }, 1306 | "NullDateTime": { 1307 | "type": "string", 1308 | "format": "date-time", 1309 | "x-go-type": "github.com/lazada/swgen.NullDateTime" 1310 | }, 1311 | "NullFloat64": { 1312 | "type": "number", 1313 | "format": "float", 1314 | "x-go-type": "github.com/lazada/swgen.NullFloat64" 1315 | }, 1316 | "NullInt64": { 1317 | "type": "integer", 1318 | "format": "int64", 1319 | "x-go-type": "github.com/lazada/swgen.NullInt64" 1320 | }, 1321 | "NullString": { 1322 | "type": "string", 1323 | "x-go-type": "github.com/lazada/swgen.NullString" 1324 | }, 1325 | "NullTimestamp": { 1326 | "type": "integer", 1327 | "format": "int64", 1328 | "x-go-type": "github.com/lazada/swgen.NullTimestamp" 1329 | }, 1330 | "NullTypes": { 1331 | "type": "object", 1332 | "properties": { 1333 | "null_bool": { 1334 | "$ref": "#/definitions/NullBool" 1335 | }, 1336 | "null_date": { 1337 | "$ref": "#/definitions/NullDate" 1338 | }, 1339 | "null_date_time": { 1340 | "$ref": "#/definitions/NullDateTime" 1341 | }, 1342 | "null_float": { 1343 | "$ref": "#/definitions/NullFloat64" 1344 | }, 1345 | "null_int": { 1346 | "$ref": "#/definitions/NullInt64" 1347 | }, 1348 | "null_string": { 1349 | "$ref": "#/definitions/NullString" 1350 | }, 1351 | "null_timestamp": { 1352 | "$ref": "#/definitions/NullTimestamp" 1353 | } 1354 | }, 1355 | "x-go-type": "github.com/lazada/swgen.NullTypes", 1356 | "x-go-property-names": { 1357 | "null_bool": "Bool", 1358 | "null_date": "Date", 1359 | "null_date_time": "DateTime", 1360 | "null_float": "Float", 1361 | "null_int": "Int", 1362 | "null_string": "String", 1363 | "null_timestamp": "Timestamp" 1364 | }, 1365 | "x-go-property-types": { 1366 | "null_bool": "github.com/lazada/swgen.NullBool", 1367 | "null_date": "github.com/lazada/swgen.NullDate", 1368 | "null_date_time": "github.com/lazada/swgen.NullDateTime", 1369 | "null_float": "github.com/lazada/swgen.NullFloat64", 1370 | "null_int": "github.com/lazada/swgen.NullInt64", 1371 | "null_string": "github.com/lazada/swgen.NullString", 1372 | "null_timestamp": "github.com/lazada/swgen.NullTimestamp" 1373 | } 1374 | }, 1375 | "TestSampleStruct": { 1376 | "type": "object", 1377 | "properties": { 1378 | "simple_int": { 1379 | "type": "integer", 1380 | "format": "int32", 1381 | "x-go-type": "int" 1382 | }, 1383 | "simple_string": { 1384 | "type": "string", 1385 | "x-go-type": "string" 1386 | } 1387 | }, 1388 | "x-go-type": "github.com/lazada/swgen.TestSampleStruct", 1389 | "x-go-property-names": { 1390 | "simple_int": "SimpleInt", 1391 | "simple_string": "SimpleString" 1392 | }, 1393 | "x-go-property-types": { 1394 | "simple_int": "int", 1395 | "simple_string": "string" 1396 | } 1397 | }, 1398 | "TestSampleStructType2": { 1399 | "type": "object", 1400 | "properties": { 1401 | "simple_bool": { 1402 | "type": "boolean", 1403 | "x-go-type": "bool" 1404 | }, 1405 | "simple_float64": { 1406 | "type": "number", 1407 | "format": "double", 1408 | "x-go-type": "float64" 1409 | } 1410 | }, 1411 | "x-go-type": "github.com/lazada/swgen/sample.TestSampleStruct", 1412 | "x-go-property-names": { 1413 | "simple_bool": "SimpleBool", 1414 | "simple_float64": "SimpleFloat64" 1415 | }, 1416 | "x-go-property-types": { 1417 | "simple_bool": "bool", 1418 | "simple_float64": "float64" 1419 | } 1420 | }, 1421 | "Unknown": { 1422 | "type": "object", 1423 | "properties": { 1424 | "anything": { 1425 | "x-go-type": "::interface {}" 1426 | }, 1427 | "whatever": { 1428 | "x-go-type": "*[]uint8" 1429 | } 1430 | }, 1431 | "x-go-type": "github.com/lazada/swgen.Unknown", 1432 | "x-go-property-names": { 1433 | "anything": "Anything", 1434 | "whatever": "Whatever" 1435 | }, 1436 | "x-go-property-types": { 1437 | "anything": "::interface {}", 1438 | "whatever": "*[]uint8" 1439 | } 1440 | }, 1441 | "deepReplacementTag": { 1442 | "type": "object", 1443 | "properties": { 1444 | "test_field_1": { 1445 | "type": "number", 1446 | "format": "double", 1447 | "x-go-type": "string" 1448 | } 1449 | }, 1450 | "x-go-type": "github.com/lazada/swgen.deepReplacementTag", 1451 | "x-go-property-names": { 1452 | "test_field_1": "TestField1" 1453 | }, 1454 | "x-go-property-types": { 1455 | "test_field_1": "string" 1456 | } 1457 | }, 1458 | "definitionExample": { 1459 | "type": "string", 1460 | "format": "byte", 1461 | "x-go-type": "github.com/lazada/swgen.definitionExample" 1462 | }, 1463 | "mapDateTime": { 1464 | "type": "object", 1465 | "properties": { 1466 | "items": { 1467 | "type": "object", 1468 | "additionalProperties": { 1469 | "$ref": "#/definitions/simpleDateTime" 1470 | }, 1471 | "x-go-type": "map[string]github.com/lazada/swgen.simpleDateTime" 1472 | } 1473 | }, 1474 | "x-go-type": "github.com/lazada/swgen.mapDateTime", 1475 | "x-go-property-names": { 1476 | "items": "Items" 1477 | }, 1478 | "x-go-property-types": { 1479 | "items": "map[string]github.com/lazada/swgen.simpleDateTime" 1480 | } 1481 | }, 1482 | "mixedStruct": { 1483 | "type": "object", 1484 | "properties": { 1485 | "anonProp": { 1486 | "type": "integer", 1487 | "format": "int32", 1488 | "x-go-type": "int" 1489 | }, 1490 | "fieldBody": { 1491 | "type": "integer", 1492 | "format": "int32", 1493 | "x-go-type": "int" 1494 | } 1495 | }, 1496 | "x-go-type": "github.com/lazada/swgen.mixedStruct", 1497 | "x-go-property-names": { 1498 | "anonProp": "AnonProp", 1499 | "fieldBody": "FieldBody" 1500 | }, 1501 | "x-go-property-types": { 1502 | "anonProp": "int", 1503 | "fieldBody": "int" 1504 | } 1505 | }, 1506 | "simpleDateTime": { 1507 | "type": "object", 1508 | "properties": { 1509 | "time": { 1510 | "type": "string", 1511 | "format": "date-time", 1512 | "x-go-type": "time.Time" 1513 | } 1514 | }, 1515 | "x-go-type": "github.com/lazada/swgen.simpleDateTime", 1516 | "x-go-property-names": { 1517 | "time": "Time" 1518 | }, 1519 | "x-go-property-types": { 1520 | "time": "time.Time" 1521 | } 1522 | }, 1523 | "sliceDateTime": { 1524 | "type": "object", 1525 | "properties": { 1526 | "items": { 1527 | "type": "array", 1528 | "items": { 1529 | "$ref": "#/definitions/simpleDateTime" 1530 | }, 1531 | "x-go-type": "[]github.com/lazada/swgen.simpleDateTime" 1532 | } 1533 | }, 1534 | "x-go-type": "github.com/lazada/swgen.sliceDateTime", 1535 | "x-go-property-names": { 1536 | "items": "Items" 1537 | }, 1538 | "x-go-property-types": { 1539 | "items": "[]github.com/lazada/swgen.simpleDateTime" 1540 | } 1541 | }, 1542 | "testDefaults": { 1543 | "type": "object", 1544 | "properties": { 1545 | "field1": { 1546 | "default": 25, 1547 | "type": "integer", 1548 | "format": "int32", 1549 | "x-go-type": "int" 1550 | }, 1551 | "field2": { 1552 | "default": 25.5, 1553 | "type": "number", 1554 | "format": "double", 1555 | "x-go-type": "float64" 1556 | }, 1557 | "field3": { 1558 | "default": "test", 1559 | "type": "string", 1560 | "x-go-type": "string" 1561 | }, 1562 | "field4": { 1563 | "default": true, 1564 | "type": "boolean", 1565 | "x-go-type": "bool" 1566 | }, 1567 | "field5": { 1568 | "default": [ 1569 | 1, 1570 | 2, 1571 | 3 1572 | ], 1573 | "type": "array", 1574 | "items": { 1575 | "type": "integer", 1576 | "format": "int32", 1577 | "x-go-type": "int" 1578 | }, 1579 | "x-go-type": "[]int" 1580 | }, 1581 | "field6": { 1582 | "default": { 1583 | "test": 1 1584 | }, 1585 | "type": "object", 1586 | "additionalProperties": { 1587 | "type": "integer", 1588 | "format": "int32", 1589 | "x-go-type": "int" 1590 | }, 1591 | "x-go-type": "map[string]int" 1592 | }, 1593 | "field7": { 1594 | "default": 25, 1595 | "type": "integer", 1596 | "format": "int32", 1597 | "x-go-type": "*uint" 1598 | } 1599 | }, 1600 | "x-go-type": "github.com/lazada/swgen.testDefaults", 1601 | "x-go-property-names": { 1602 | "field1": "Field1", 1603 | "field2": "Field2", 1604 | "field3": "Field3", 1605 | "field4": "Field4", 1606 | "field5": "Field5", 1607 | "field6": "Field6", 1608 | "field7": "Field7" 1609 | }, 1610 | "x-go-property-types": { 1611 | "field1": "int", 1612 | "field2": "float64", 1613 | "field3": "string", 1614 | "field4": "bool", 1615 | "field5": "[]int", 1616 | "field6": "map[string]int", 1617 | "field7": "*uint" 1618 | } 1619 | }, 1620 | "testEmptyStruct": { 1621 | "type": "object", 1622 | "x-go-type": "github.com/lazada/swgen.testEmptyStruct" 1623 | }, 1624 | "testSimpleMapList": { 1625 | "type": "object", 1626 | "properties": { 1627 | "map_list_bool": { 1628 | "type": "array", 1629 | "items": { 1630 | "type": "object", 1631 | "additionalProperties": { 1632 | "type": "boolean", 1633 | "x-go-type": "bool" 1634 | }, 1635 | "x-go-type": "map[string]bool" 1636 | }, 1637 | "x-go-type": "[]map[string]bool" 1638 | }, 1639 | "map_list_float32": { 1640 | "type": "array", 1641 | "items": { 1642 | "type": "object", 1643 | "additionalProperties": { 1644 | "type": "number", 1645 | "format": "float", 1646 | "x-go-type": "float32" 1647 | }, 1648 | "x-go-type": "map[string]float32" 1649 | }, 1650 | "x-go-type": "[]map[string]float32" 1651 | }, 1652 | "map_list_float64": { 1653 | "type": "array", 1654 | "items": { 1655 | "type": "object", 1656 | "additionalProperties": { 1657 | "type": "number", 1658 | "format": "double", 1659 | "x-go-type": "float64" 1660 | }, 1661 | "x-go-type": "map[string]float64" 1662 | }, 1663 | "x-go-type": "[]map[string]float64" 1664 | }, 1665 | "map_list_int": { 1666 | "type": "array", 1667 | "items": { 1668 | "type": "object", 1669 | "additionalProperties": { 1670 | "type": "integer", 1671 | "format": "int32", 1672 | "x-go-type": "int" 1673 | }, 1674 | "x-go-type": "map[string]int" 1675 | }, 1676 | "x-go-type": "[]map[string]int" 1677 | }, 1678 | "map_list_int32": { 1679 | "type": "array", 1680 | "items": { 1681 | "type": "object", 1682 | "additionalProperties": { 1683 | "type": "integer", 1684 | "format": "int32", 1685 | "x-go-type": "int32" 1686 | }, 1687 | "x-go-type": "map[string]int32" 1688 | }, 1689 | "x-go-type": "[]map[string]int32" 1690 | }, 1691 | "map_list_int64": { 1692 | "type": "array", 1693 | "items": { 1694 | "type": "object", 1695 | "additionalProperties": { 1696 | "type": "integer", 1697 | "format": "int64", 1698 | "x-go-type": "int64" 1699 | }, 1700 | "x-go-type": "map[string]int64" 1701 | }, 1702 | "x-go-type": "[]map[string]int64" 1703 | }, 1704 | "map_list_string": { 1705 | "type": "array", 1706 | "items": { 1707 | "type": "object", 1708 | "additionalProperties": { 1709 | "type": "string", 1710 | "x-go-type": "string" 1711 | }, 1712 | "x-go-type": "map[string]string" 1713 | }, 1714 | "x-go-type": "[]map[string]string" 1715 | }, 1716 | "map_list_uint32": { 1717 | "type": "array", 1718 | "items": { 1719 | "type": "object", 1720 | "additionalProperties": { 1721 | "type": "integer", 1722 | "format": "int64", 1723 | "x-go-type": "uint32" 1724 | }, 1725 | "x-go-type": "map[string]uint32" 1726 | }, 1727 | "x-go-type": "[]map[string]uint32" 1728 | }, 1729 | "map_list_uint64": { 1730 | "type": "array", 1731 | "items": { 1732 | "type": "object", 1733 | "additionalProperties": { 1734 | "type": "integer", 1735 | "format": "int64", 1736 | "x-go-type": "uint64" 1737 | }, 1738 | "x-go-type": "map[string]uint64" 1739 | }, 1740 | "x-go-type": "[]map[string]uint64" 1741 | } 1742 | }, 1743 | "x-go-type": "github.com/lazada/swgen.testSimpleMapList", 1744 | "x-go-property-names": { 1745 | "map_list_bool": "MapListBool", 1746 | "map_list_float32": "MapListFloat32", 1747 | "map_list_float64": "MapListFloat64", 1748 | "map_list_int": "MapListInt", 1749 | "map_list_int32": "MapListInt32", 1750 | "map_list_int64": "MapListInt64", 1751 | "map_list_string": "MapListString", 1752 | "map_list_uint32": "MapListUInt32", 1753 | "map_list_uint64": "MapListUInt64" 1754 | }, 1755 | "x-go-property-types": { 1756 | "map_list_bool": "[]map[string]bool", 1757 | "map_list_float32": "[]map[string]float32", 1758 | "map_list_float64": "[]map[string]float64", 1759 | "map_list_int": "[]map[string]int", 1760 | "map_list_int32": "[]map[string]int32", 1761 | "map_list_int64": "[]map[string]int64", 1762 | "map_list_string": "[]map[string]string", 1763 | "map_list_uint32": "[]map[string]uint32", 1764 | "map_list_uint64": "[]map[string]uint64" 1765 | } 1766 | }, 1767 | "testSimpleMaps": { 1768 | "type": "object", 1769 | "properties": { 1770 | "map_bool": { 1771 | "type": "object", 1772 | "additionalProperties": { 1773 | "type": "boolean", 1774 | "x-go-type": "bool" 1775 | }, 1776 | "x-go-type": "map[string]bool" 1777 | }, 1778 | "map_float32": { 1779 | "type": "object", 1780 | "additionalProperties": { 1781 | "type": "number", 1782 | "format": "float", 1783 | "x-go-type": "float32" 1784 | }, 1785 | "x-go-type": "map[string]float32" 1786 | }, 1787 | "map_float64": { 1788 | "type": "object", 1789 | "additionalProperties": { 1790 | "type": "number", 1791 | "format": "double", 1792 | "x-go-type": "float64" 1793 | }, 1794 | "x-go-type": "map[string]float64" 1795 | }, 1796 | "map_int": { 1797 | "type": "object", 1798 | "additionalProperties": { 1799 | "type": "integer", 1800 | "format": "int32", 1801 | "x-go-type": "int" 1802 | }, 1803 | "x-go-type": "map[string]int" 1804 | }, 1805 | "map_int32": { 1806 | "type": "object", 1807 | "additionalProperties": { 1808 | "type": "integer", 1809 | "format": "int32", 1810 | "x-go-type": "int32" 1811 | }, 1812 | "x-go-type": "map[string]int32" 1813 | }, 1814 | "map_int64": { 1815 | "type": "object", 1816 | "additionalProperties": { 1817 | "type": "integer", 1818 | "format": "int64", 1819 | "x-go-type": "int64" 1820 | }, 1821 | "x-go-type": "map[string]int64" 1822 | }, 1823 | "map_string": { 1824 | "type": "object", 1825 | "additionalProperties": { 1826 | "type": "string", 1827 | "x-go-type": "string" 1828 | }, 1829 | "x-go-type": "map[string]string" 1830 | }, 1831 | "map_uint32": { 1832 | "type": "object", 1833 | "additionalProperties": { 1834 | "type": "integer", 1835 | "format": "int64", 1836 | "x-go-type": "uint32" 1837 | }, 1838 | "x-go-type": "map[string]uint32" 1839 | }, 1840 | "map_uint64": { 1841 | "type": "object", 1842 | "additionalProperties": { 1843 | "type": "integer", 1844 | "format": "int64", 1845 | "x-go-type": "uint64" 1846 | }, 1847 | "x-go-type": "map[string]uint64" 1848 | } 1849 | }, 1850 | "x-go-type": "github.com/lazada/swgen.testSimpleMaps", 1851 | "x-go-property-names": { 1852 | "map_bool": "MapBool", 1853 | "map_float32": "MapFloat32", 1854 | "map_float64": "MapFloat64", 1855 | "map_int": "MapInt", 1856 | "map_int32": "MapInt32", 1857 | "map_int64": "MapInt64", 1858 | "map_string": "MapString", 1859 | "map_uint32": "MapUInt32", 1860 | "map_uint64": "MapUInt64" 1861 | }, 1862 | "x-go-property-types": { 1863 | "map_bool": "map[string]bool", 1864 | "map_float32": "map[string]float32", 1865 | "map_float64": "map[string]float64", 1866 | "map_int": "map[string]int", 1867 | "map_int32": "map[string]int32", 1868 | "map_int64": "map[string]int64", 1869 | "map_string": "map[string]string", 1870 | "map_uint32": "map[string]uint32", 1871 | "map_uint64": "map[string]uint64" 1872 | } 1873 | }, 1874 | "testSimpleSlices": { 1875 | "type": "object", 1876 | "properties": { 1877 | "list_bool": { 1878 | "type": "array", 1879 | "items": { 1880 | "type": "boolean", 1881 | "x-go-type": "bool" 1882 | }, 1883 | "x-go-type": "[]bool" 1884 | }, 1885 | "list_float32": { 1886 | "type": "array", 1887 | "items": { 1888 | "type": "number", 1889 | "format": "float", 1890 | "x-go-type": "float32" 1891 | }, 1892 | "x-go-type": "[]float32" 1893 | }, 1894 | "list_float64": { 1895 | "type": "array", 1896 | "items": { 1897 | "type": "number", 1898 | "format": "double", 1899 | "x-go-type": "float64" 1900 | }, 1901 | "x-go-type": "[]float64" 1902 | }, 1903 | "list_int": { 1904 | "type": "array", 1905 | "items": { 1906 | "type": "integer", 1907 | "format": "int32", 1908 | "x-go-type": "int" 1909 | }, 1910 | "x-go-type": "[]int" 1911 | }, 1912 | "list_int32": { 1913 | "type": "array", 1914 | "items": { 1915 | "type": "integer", 1916 | "format": "int32", 1917 | "x-go-type": "int32" 1918 | }, 1919 | "x-go-type": "[]int32" 1920 | }, 1921 | "list_int64": { 1922 | "type": "array", 1923 | "items": { 1924 | "type": "integer", 1925 | "format": "int64", 1926 | "x-go-type": "int64" 1927 | }, 1928 | "x-go-type": "[]int64" 1929 | }, 1930 | "list_string": { 1931 | "type": "array", 1932 | "items": { 1933 | "type": "string", 1934 | "x-go-type": "string" 1935 | }, 1936 | "x-go-type": "[]string" 1937 | }, 1938 | "list_uint32": { 1939 | "type": "array", 1940 | "items": { 1941 | "type": "integer", 1942 | "format": "int64", 1943 | "x-go-type": "uint32" 1944 | }, 1945 | "x-go-type": "[]uint32" 1946 | }, 1947 | "list_uint64": { 1948 | "type": "array", 1949 | "items": { 1950 | "type": "integer", 1951 | "format": "int64", 1952 | "x-go-type": "uint64" 1953 | }, 1954 | "x-go-type": "[]uint64" 1955 | } 1956 | }, 1957 | "x-go-type": "github.com/lazada/swgen.testSimpleSlices", 1958 | "x-go-property-names": { 1959 | "list_bool": "ListBool", 1960 | "list_float32": "ListFloat32", 1961 | "list_float64": "ListFloat64", 1962 | "list_int": "ListInt", 1963 | "list_int32": "ListInt32", 1964 | "list_int64": "ListInt64", 1965 | "list_string": "ListString", 1966 | "list_uint32": "ListUInt32", 1967 | "list_uint64": "ListUInt64" 1968 | }, 1969 | "x-go-property-types": { 1970 | "list_bool": "[]bool", 1971 | "list_float32": "[]float32", 1972 | "list_float64": "[]float64", 1973 | "list_int": "[]int", 1974 | "list_int32": "[]int32", 1975 | "list_int64": "[]int64", 1976 | "list_string": "[]string", 1977 | "list_uint32": "[]uint32", 1978 | "list_uint64": "[]uint64" 1979 | } 1980 | }, 1981 | "testSimpleStruct": { 1982 | "type": "object", 1983 | "properties": { 1984 | "simple_bool": { 1985 | "type": "boolean", 1986 | "x-go-type": "bool" 1987 | }, 1988 | "simple_float32": { 1989 | "type": "number", 1990 | "format": "float", 1991 | "x-go-type": "float32" 1992 | }, 1993 | "simple_float64": { 1994 | "type": "number", 1995 | "format": "double", 1996 | "x-go-type": "float64" 1997 | }, 1998 | "simple_int": { 1999 | "type": "integer", 2000 | "format": "int32", 2001 | "x-go-type": "int" 2002 | }, 2003 | "simple_int32": { 2004 | "type": "integer", 2005 | "format": "int32", 2006 | "x-go-type": "int32" 2007 | }, 2008 | "simple_int64": { 2009 | "type": "integer", 2010 | "format": "int64", 2011 | "x-go-type": "int64" 2012 | }, 2013 | "simple_string": { 2014 | "type": "string", 2015 | "x-go-type": "string" 2016 | }, 2017 | "simple_uint32": { 2018 | "type": "integer", 2019 | "format": "int64", 2020 | "x-go-type": "uint32" 2021 | }, 2022 | "simple_uint64": { 2023 | "type": "integer", 2024 | "format": "int64", 2025 | "x-go-type": "uint64" 2026 | } 2027 | }, 2028 | "x-go-type": "github.com/lazada/swgen.testSimpleStruct", 2029 | "x-go-property-names": { 2030 | "simple_bool": "SimpleBool", 2031 | "simple_float32": "SimpleFloat32", 2032 | "simple_float64": "SimpleFloat64", 2033 | "simple_int": "SimpleInt", 2034 | "simple_int32": "SimpleInt32", 2035 | "simple_int64": "SimpleInt64", 2036 | "simple_string": "SimpleString", 2037 | "simple_uint32": "SimpleUInt32", 2038 | "simple_uint64": "SimpleUInt64" 2039 | }, 2040 | "x-go-property-types": { 2041 | "simple_bool": "bool", 2042 | "simple_float32": "float32", 2043 | "simple_float64": "float64", 2044 | "simple_int": "int", 2045 | "simple_int32": "int32", 2046 | "simple_int64": "int64", 2047 | "simple_string": "string", 2048 | "simple_uint32": "uint32", 2049 | "simple_uint64": "uint64" 2050 | } 2051 | }, 2052 | "testSubTypes": { 2053 | "type": "object", 2054 | "properties": { 2055 | "test_simple_map_list": { 2056 | "$ref": "#/definitions/testSimpleMapList" 2057 | }, 2058 | "test_simple_maps": { 2059 | "$ref": "#/definitions/testSimpleMaps" 2060 | }, 2061 | "test_simple_slices": { 2062 | "$ref": "#/definitions/testSimpleSlices" 2063 | }, 2064 | "test_simple_struct": { 2065 | "$ref": "#/definitions/testSimpleStruct" 2066 | } 2067 | }, 2068 | "x-go-type": "github.com/lazada/swgen.testSubTypes", 2069 | "x-go-property-names": { 2070 | "test_simple_map_list": "TestSimpleMapList", 2071 | "test_simple_maps": "TestSimpleMaps", 2072 | "test_simple_slices": "TestSimpleSlices", 2073 | "test_simple_struct": "TestSimpleStruct" 2074 | }, 2075 | "x-go-property-types": { 2076 | "test_simple_map_list": "github.com/lazada/swgen.testSimpleMapList", 2077 | "test_simple_maps": "github.com/lazada/swgen.testSimpleMaps", 2078 | "test_simple_slices": "github.com/lazada/swgen.testSimpleSlices", 2079 | "test_simple_struct": "github.com/lazada/swgen.testSimpleStruct" 2080 | } 2081 | }, 2082 | "testWrapParams": { 2083 | "type": "object", 2084 | "properties": { 2085 | "deep_replacement": { 2086 | "$ref": "#/definitions/deepReplacementTag" 2087 | }, 2088 | "should_be_sting": { 2089 | "type": "string", 2090 | "x-go-type": "int" 2091 | }, 2092 | "simple_test_replacement": { 2093 | "$ref": "#/definitions/simpleTestReplacement" 2094 | } 2095 | }, 2096 | "x-go-type": "github.com/lazada/swgen.testWrapParams", 2097 | "x-go-property-names": { 2098 | "deep_replacement": "DeepReplacementTag", 2099 | "should_be_sting": "ReplaceByTag", 2100 | "simple_test_replacement": "SimpleTestReplacement" 2101 | }, 2102 | "x-go-property-types": { 2103 | "deep_replacement": "github.com/lazada/swgen.deepReplacementTag", 2104 | "should_be_sting": "int", 2105 | "simple_test_replacement": "github.com/lazada/swgen.simpleTestReplacement" 2106 | } 2107 | }, 2108 | "typeMap": { 2109 | "type": "object", 2110 | "additionalProperties": { 2111 | "type": "integer", 2112 | "format": "int32", 2113 | "x-go-type": "int" 2114 | }, 2115 | "x-go-type": "map[string]int" 2116 | }, 2117 | "typeMapHolder": { 2118 | "type": "object", 2119 | "properties": { 2120 | "m": { 2121 | "$ref": "#/definitions/typeMap" 2122 | } 2123 | }, 2124 | "x-go-type": "github.com/lazada/swgen.typeMapHolder", 2125 | "x-go-property-names": { 2126 | "m": "M" 2127 | }, 2128 | "x-go-property-types": { 2129 | "m": "github.com/lazada/swgen.typeMap" 2130 | } 2131 | } 2132 | }, 2133 | "x-service-type": "rest" 2134 | } --------------------------------------------------------------------------------