├── .gitignore
├── .travis.yml
├── README.md
├── build.cmd
├── gen_client.cmd
├── gen_client_bitbucket20.cmd
├── pom.xml
├── regen_project.cmd
└── src
└── main
├── java
└── uk
│ └── co
│ └── itofinity
│ └── codegen
│ ├── AbstractCSharpCodegen.java
│ ├── CSharpRefitClientCodegen.java
│ └── CsharprefitcodegenGenerator.java
└── resources
├── META-INF
└── services
│ └── io.swagger.codegen.CodegenConfig
└── csharprefit
├── ApiClient.mustache
├── Project.mustache
├── README.mustache
├── Solution.mustache
├── TestProject.mustache
├── api.mustache
├── api_doc.mustache
├── api_test.mustache
├── appveyor.mustache
├── enumClass.mustache
├── gitignore.mustache
├── model.mustache
├── modelEnum.mustache
├── modelGeneric.mustache
├── modelInnerEnum.mustache
├── model_doc.mustache
├── model_test.mustache
├── mono_nunit_test.mustache
├── partial_header.mustache
├── shared_project.mustache
├── shared_projitems.mustache
└── visibility.mustache
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | clients/
3 | gen_client_petstore.cmd
4 | gen_client_bitbucket20.cmd
5 | target/
6 | tools/swagger-codegen-cli-2.2.3.jar
7 | CSharpRefitCodegen-swagger-codegen.iml
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | deploy:
3 | provider: releases
4 | api_key:
5 | secure: m6FYT4fT3/qhjvp/X65MnJPVDgjOrALsZsosBwLALyV+03ReiiJHQxSCepogGMD1QyLw/v6V4q7NJHtezxkDkjx+7UYMqiwj5/R+zcltIryA8Upjv8agxQJiBkRHjXZtlDom+xD3fmYDzv/02KnvK7ysygSkOL8n9oAGzeRCL98DlBpbpQslYmGTKVU2muyid5+uoX4gc1ZyNV+V42fI8biP0yRJdsjgEsjQjQ8RH4qg8/agRHBGOcNW/TXCuSnUgSA3EcKwaK72+yvZWLIIvJO9nPilTH0Ndzl3ykY3HWq1o8nA6NUbXSnKvo7l/XNcc6jTMIx14snXYgIGlXSCA6CTtegZigY1Ad3c+/4otijCezivNQqNwmbcIDaHTslCrOheLaXPGaceWYnnbu0Yi/pkIwpCNugWBwcNvyx88MfboUns17ZYIpBeKcIxAF0Fiami0eJfboYoPcrVSOpMcYj+Wv9gSEgGGvCX0A5duBZ6ZlnLAGW+MBSsm+gqvAhUNOlGxvEh6RFj3fX1eMJVoBXJjrwgV4T6Xl/v+tX1wwTcWjf17ygNHPIVyIUp1PichX5OK5KBfJYhsLMPS6/bujZwwl69HF7v+fUsBWXcmo+gzYyl0IwSQzApd5Oyk/Nr6SQ3oA4rGFHfxImQGHN+ju6g8tgb41g2g4fkpP/DUAs=
6 | file: /home/travis/build/itofinity/swagger-csharp-refit/target/CSharpRefitCodegen-swagger-codegen-1.0.0.jar
7 | skip_cleanup: true
8 | on:
9 | repo: itofinity/swagger-csharp-refit
10 | tags: true
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | swaggger_csharp_refit\# Swagger Codegen for the CSharpRefitCodegen library
2 |
3 | ## Overview
4 | This is a generator for building C# Refit based REST API clinet libraries from swagger.json files.
5 |
6 | ### Why?
7 | The existing Swagger code generators use [RestSharp](http://restsharp.org/) which is great, but I personally find bloated. I prefer the lightweight [Refit](https://github.com/paulcbetts/refit) approach.
8 | The existing Swagger code generators for C# do not handle snake_case object names used by non-C# languages in their swagger.json.
9 |
10 | ## What's Swagger?
11 | The goal of Swagger� is to define a standard, language-agnostic interface to REST APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined via Swagger, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. Similar to what interfaces have done for lower-level programming, Swagger removes the guesswork in calling the service.
12 |
13 |
14 | Check out [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) for additional information about the Swagger project, including additional libraries with support for other languages and more.
15 |
16 | ## Usage
17 |
18 | ### To build the generator
19 | Clone this repository.
20 |
21 | Once inside the repository folder clone the submodule:
22 | ```
23 | git submodule init
24 | git submodule update
25 | ```
26 | and then in the swagger-csharp-refit folder run (you will need Java + Maven installed):
27 | ```
28 | build.cmd
29 | ```
30 | or
31 | ```
32 | mvn clean package
33 | ```
34 | This will generate the codge generator
35 | ```
36 | target/CSharpRefitCodegen-swagger-codegen-1.0.0.jar
37 | ```
38 |
39 | ### To run the generator
40 | #### From the repository
41 | First build the generator, see above.
42 | Then download the [Swagger Codegen Cli](http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.3/swagger-codegen-cli-2.2.3.jar) into the.\tools folder.
43 | Then run:
44 | ```
45 | gen_client.cmd -i {url_or_path_to_swagger_json} -o clients/{client_name}
46 | ```
47 | or
48 | ```
49 | java -cp target/CSharpRefitCodegen-swagger-codegen-1.0.0.jar;tools/swagger-codegen-cli-2.2.3.jar io.swagger.codegen.SwaggerCodegen generate -l csharprefit -i {url_or_path_to_swagger_json} -o clients/{client_name}
50 | ```
51 | #### From artifact
52 | Download the [latest jar](https://github.com/itofinity/swagger-csharp-refit/releases/latest)
53 |
54 |
55 | .\gen_client_bitbucket20.cmd gives an example of a more feature rich call.
56 |
57 | ## How was this built?
58 | The starting point was the existing CSharp generator code in swagger, this was extracted and applied over the top of a custom generator started using the [Making your own codegen modules](https://github.com/swagger-api/swagger-codegen#making-your-own-codegen-modules) process.
59 |
60 | It was tested against the Bitbucket [swagger.json](https://api.bitbucket.org/swagger.json)
61 |
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | mvn clean package
--------------------------------------------------------------------------------
/gen_client.cmd:
--------------------------------------------------------------------------------
1 | java -cp target/CSharpRefitCodegen-swagger-codegen-1.0.2.jar;tools/swagger-codegen-cli-2.2.3.jar io.swagger.codegen.SwaggerCodegen generate -l csharprefit %*
--------------------------------------------------------------------------------
/gen_client_bitbucket20.cmd:
--------------------------------------------------------------------------------
1 | rmdir /S /Q clients\bitbucket
2 | set PROJ_NAME=Itofinity.Bitbucket.Rest.Refit
3 | rem rmdir /S /Q ..\%PROJ_NAME%
4 | call gen_client.cmd ^
5 | -i https://api.bitbucket.org/swagger.json ^
6 | -o ^
7 | clients/%PROJ_NAME% --api-package Api --model-package Model -DpackageName=%PROJ_NAME%
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | io.swagger
5 | CSharpRefitCodegen-swagger-codegen
6 | jar
7 | CSharpRefitCodegen-swagger-codegen
8 | 1.0.2
9 |
10 | 2.2.0
11 |
12 |
13 |
14 |
15 | org.apache.maven.plugins
16 | maven-surefire-plugin
17 | 2.12
18 |
19 |
20 |
21 | loggerPath
22 | conf/log4j.properties
23 |
24 |
25 | -Xms512m -Xmx1500m
26 | methods
27 | pertest
28 |
29 |
30 |
31 |
32 |
33 | org.apache.maven.plugins
34 | maven-jar-plugin
35 | 2.2
36 |
37 |
38 |
39 | jar
40 | test-jar
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | org.codehaus.mojo
50 | build-helper-maven-plugin
51 |
52 |
53 | add_sources
54 | generate-sources
55 |
56 | add-source
57 |
58 |
59 |
60 | src/main/java
61 |
62 |
63 |
64 |
65 | add_test_sources
66 | generate-test-sources
67 |
68 | add-test-source
69 |
70 |
71 |
72 | src/test/java
73 |
74 |
75 |
76 |
77 |
78 |
79 | org.apache.maven.plugins
80 | maven-compiler-plugin
81 | 3.6.1
82 |
83 | 1.7
84 | 1.7
85 |
86 |
87 |
88 |
89 |
90 |
91 | io.swagger
92 | swagger-codegen
93 | ${swagger-codegen-version}
94 | provided
95 |
96 |
97 |
98 | 2.2.3
99 | 1.0.0
100 | 4.8.1
101 |
102 |
103 |
--------------------------------------------------------------------------------
/regen_project.cmd:
--------------------------------------------------------------------------------
1 | java -jar .\tools\swagger-codegen-cli-2.2.3.jar meta -o . -n CSharpRefitCodegen -p uk.co.itofinity.codegen
--------------------------------------------------------------------------------
/src/main/java/uk/co/itofinity/codegen/AbstractCSharpCodegen.java:
--------------------------------------------------------------------------------
1 | package uk.co.itofinity.codegen;
2 |
3 | import io.swagger.codegen.*;
4 | import io.swagger.models.properties.*;
5 | import org.apache.commons.lang3.StringUtils;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.io.File;
10 | import java.util.*;
11 |
12 | public abstract class AbstractCSharpCodegen extends DefaultCodegen implements CodegenConfig {
13 |
14 | protected boolean optionalProjectFileFlag = true;
15 | protected boolean optionalEmitDefaultValue = false;
16 | protected boolean optionalMethodArgumentFlag = true;
17 | protected boolean useDateTimeOffsetFlag = false;
18 | protected boolean useCollection = false;
19 | protected boolean returnICollection = false;
20 | protected boolean netCoreProjectFileFlag = false;
21 |
22 | protected String packageVersion = "1.0.0";
23 | protected String packageName = "IO.Swagger";
24 | protected String packageTitle = "Swagger Library";
25 | protected String packageProductName = "SwaggerLibrary";
26 | protected String packageDescription = "A library generated from a Swagger doc";
27 | protected String packageCompany = "Swagger";
28 | protected String packageCopyright = "No Copyright";
29 | protected String packageAuthors = "Swagger";
30 |
31 | protected String interfacePrefix = "I";
32 |
33 | protected String sourceFolder = "src";
34 |
35 | // TODO: Add option for test folder output location. Nice to allow e.g. ./test instead of ./src.
36 | // This would require updating relative paths (e.g. path to main project file in test project file)
37 | protected String testFolder = sourceFolder;
38 |
39 | protected Set collectionTypes;
40 | protected Set mapTypes;
41 |
42 | protected Logger LOGGER = LoggerFactory.getLogger(AbstractCSharpCodegen.class);
43 |
44 | public AbstractCSharpCodegen() {
45 | super();
46 |
47 | // C# does not use import mapping
48 | importMapping.clear();
49 |
50 | outputFolder = "generated-code" + File.separator + this.getName();
51 | embeddedTemplateDir = templateDir = this.getName();
52 |
53 | collectionTypes = new HashSet(
54 | Arrays.asList(
55 | "IList", "List",
56 | "ICollection", "Collection",
57 | "IEnumerable")
58 | );
59 |
60 | mapTypes = new HashSet(
61 | Arrays.asList("IDictionary")
62 | );
63 |
64 | setReservedWordsLowerCase(
65 | Arrays.asList(
66 | // set "client" as a reserved word to avoid conflicts with IO.Swagger.Client
67 | // this is a workaround and can be removed if c# api client is updated to use
68 | // fully qualified name
69 | "client", "parameter",
70 | // local variable names in API methods (endpoints)
71 | "localVarPath", "localVarPathParams", "localVarQueryParams", "localVarHeaderParams",
72 | "localVarFormParams", "localVarFileParams", "localVarStatusCode", "localVarResponse",
73 | "localVarPostBody", "localVarHttpHeaderAccepts", "localVarHttpHeaderAccept",
74 | "localVarHttpContentTypes", "localVarHttpContentType",
75 | "localVarStatusCode",
76 | // C# reserved words
77 | "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked",
78 | "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else",
79 | "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for",
80 | "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
81 | "long", "namespace", "new", "null", "object", "operator", "out", "override", "params",
82 | "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed",
83 | "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw",
84 | "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
85 | "virtual", "void", "volatile", "while", "version")
86 | );
87 |
88 | // TODO: Either include fully qualified names here or handle in DefaultCodegen via lastIndexOf(".") search
89 | languageSpecificPrimitives = new HashSet(
90 | Arrays.asList(
91 | "String",
92 | "string",
93 | "bool?",
94 | "double?",
95 | "decimal?",
96 | "int?",
97 | "long?",
98 | "float?",
99 | "byte[]",
100 | "ICollection",
101 | "Collection",
102 | "List",
103 | "Dictionary",
104 | "DateTime?",
105 | "DateTimeOffset?",
106 | "String",
107 | "Boolean",
108 | "Double",
109 | "Int32",
110 | "Int64",
111 | "Float",
112 | "Guid?",
113 | "System.IO.Stream", // not really a primitive, we include it to avoid model import
114 | "Object")
115 | );
116 |
117 | instantiationTypes.put("array", "List");
118 | instantiationTypes.put("list", "List");
119 | instantiationTypes.put("map", "Dictionary");
120 |
121 | // Nullable types here assume C# 2 support is not part of base
122 | typeMapping = new HashMap();
123 | typeMapping.put("string", "string");
124 | typeMapping.put("binary", "byte[]");
125 | typeMapping.put("bytearray", "byte[]");
126 | typeMapping.put("boolean", "bool?");
127 | typeMapping.put("integer", "int?");
128 | typeMapping.put("float", "float?");
129 | typeMapping.put("long", "long?");
130 | typeMapping.put("double", "double?");
131 | typeMapping.put("number", "decimal?");
132 | typeMapping.put("datetime", "DateTime?");
133 | typeMapping.put("date", "DateTime?");
134 | typeMapping.put("file", "System.IO.Stream");
135 | typeMapping.put("array", "List");
136 | typeMapping.put("list", "List");
137 | typeMapping.put("map", "Dictionary");
138 | typeMapping.put("object", "Object");
139 | typeMapping.put("uuid", "Guid?");
140 | }
141 |
142 | public void setReturnICollection(boolean returnICollection) {
143 | this.returnICollection = returnICollection;
144 | }
145 |
146 | public void setOptionalEmitDefaultValue(boolean optionalEmitDefaultValue) {
147 | this.optionalEmitDefaultValue = optionalEmitDefaultValue;
148 | }
149 |
150 | public void setUseCollection(boolean useCollection) {
151 | this.useCollection = useCollection;
152 | if (useCollection) {
153 | typeMapping.put("array", "Collection");
154 | typeMapping.put("list", "Collection");
155 |
156 | instantiationTypes.put("array", "Collection");
157 | instantiationTypes.put("list", "Collection");
158 | }
159 | }
160 |
161 | public void setOptionalMethodArgumentFlag(boolean flag) {
162 | this.optionalMethodArgumentFlag = flag;
163 | }
164 |
165 | public void setNetCoreProjectFileFlag(boolean flag) {
166 | this.netCoreProjectFileFlag = flag;
167 | }
168 |
169 | protected void addOption(String key, String description, String defaultValue) {
170 | CliOption option = new CliOption(key, description);
171 | if (defaultValue != null) option.defaultValue(defaultValue);
172 | cliOptions.add(option);
173 | }
174 |
175 | protected void addSwitch(String key, String description, Boolean defaultValue) {
176 | CliOption option = CliOption.newBoolean(key, description);
177 | if (defaultValue != null) option.defaultValue(defaultValue.toString());
178 | cliOptions.add(option);
179 | }
180 |
181 | public void useDateTimeOffset(boolean flag) {
182 | this.useDateTimeOffsetFlag = flag;
183 | if (flag) typeMapping.put("datetime", "DateTimeOffset?");
184 | else typeMapping.put("datetime", "DateTime?");
185 | }
186 |
187 | @Override
188 | public void processOpts() {
189 | super.processOpts();
190 |
191 | // {{packageVersion}}
192 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
193 | setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
194 | } else {
195 | additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
196 | }
197 |
198 | // {{sourceFolder}}
199 | if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
200 | setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
201 | } else {
202 | additionalProperties.put(CodegenConstants.SOURCE_FOLDER, this.sourceFolder);
203 | }
204 |
205 | // {{packageName}}
206 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
207 | setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
208 | } else {
209 | additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
210 | }
211 |
212 | if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
213 | LOGGER.warn(String.format("%s is not used by C# generators. Please use %s", CodegenConstants.INVOKER_PACKAGE, CodegenConstants.PACKAGE_NAME));
214 | }
215 |
216 | // {{packageTitle}}
217 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_TITLE)) {
218 | setPackageTitle((String) additionalProperties.get(CodegenConstants.PACKAGE_TITLE));
219 | } else {
220 | additionalProperties.put(CodegenConstants.PACKAGE_TITLE, packageTitle);
221 | }
222 |
223 | // {{packageProductName}}
224 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_PRODUCTNAME)) {
225 | setPackageProductName((String) additionalProperties.get(CodegenConstants.PACKAGE_PRODUCTNAME));
226 | } else {
227 | additionalProperties.put(CodegenConstants.PACKAGE_PRODUCTNAME, packageProductName);
228 | }
229 |
230 | // {{packageDescription}}
231 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_DESCRIPTION)) {
232 | setPackageDescription((String) additionalProperties.get(CodegenConstants.PACKAGE_DESCRIPTION));
233 | } else {
234 | additionalProperties.put(CodegenConstants.PACKAGE_DESCRIPTION, packageDescription);
235 | }
236 |
237 | // {{packageCompany}}
238 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_COMPANY)) {
239 | setPackageCompany((String) additionalProperties.get(CodegenConstants.PACKAGE_COMPANY));
240 | } else {
241 | additionalProperties.put(CodegenConstants.PACKAGE_COMPANY, packageCompany);
242 | }
243 |
244 | // {{packageCopyright}}
245 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_COPYRIGHT)) {
246 | setPackageCopyright((String) additionalProperties.get(CodegenConstants.PACKAGE_COPYRIGHT));
247 | } else {
248 | additionalProperties.put(CodegenConstants.PACKAGE_COPYRIGHT, packageCopyright);
249 | }
250 |
251 | // {{packageAuthors}}
252 | if (additionalProperties.containsKey(CodegenConstants.PACKAGE_AUTHORS)) {
253 | setPackageAuthors((String) additionalProperties.get(CodegenConstants.PACKAGE_AUTHORS));
254 | } else {
255 | additionalProperties.put(CodegenConstants.PACKAGE_AUTHORS, packageAuthors);
256 | }
257 |
258 | // {{useDateTimeOffset}}
259 | if (additionalProperties.containsKey(CodegenConstants.USE_DATETIME_OFFSET)) {
260 | useDateTimeOffset(Boolean.valueOf(additionalProperties.get(CodegenConstants.USE_DATETIME_OFFSET).toString()));
261 | }
262 | additionalProperties.put(CodegenConstants.USE_DATETIME_OFFSET, useDateTimeOffsetFlag);
263 |
264 | if (additionalProperties.containsKey(CodegenConstants.USE_COLLECTION)) {
265 | setUseCollection(Boolean.valueOf(additionalProperties.get(CodegenConstants.USE_COLLECTION).toString()));
266 | }
267 |
268 | if (additionalProperties.containsKey(CodegenConstants.RETURN_ICOLLECTION)) {
269 | setReturnICollection(Boolean.valueOf(additionalProperties.get(CodegenConstants.RETURN_ICOLLECTION).toString()));
270 | }
271 |
272 | if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES)) {
273 | setOptionalEmitDefaultValue(Boolean.valueOf(additionalProperties.get(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES).toString()));
274 | }
275 |
276 | if (additionalProperties.containsKey(CodegenConstants.NETCORE_PROJECT_FILE)) {
277 | setNetCoreProjectFileFlag(Boolean.valueOf(additionalProperties.get(CodegenConstants.NETCORE_PROJECT_FILE).toString()));
278 | }
279 |
280 | if (additionalProperties.containsKey(CodegenConstants.INTERFACE_PREFIX)) {
281 | String useInterfacePrefix = additionalProperties.get(CodegenConstants.INTERFACE_PREFIX).toString();
282 | if("false".equals(useInterfacePrefix.toLowerCase())) {
283 | setInterfacePrefix("");
284 | } else if(!"true".equals(useInterfacePrefix.toLowerCase())) {
285 | // NOTE: if user passes "true" explicitly, we use the default I- prefix. The other supported case here is a custom prefix.
286 | setInterfacePrefix(sanitizeName(useInterfacePrefix));
287 | }
288 | }
289 |
290 | // This either updates additionalProperties with the above fixes, or sets the default if the option was not specified.
291 | additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix);
292 | }
293 |
294 | @Override
295 | public Map postProcessModels(Map objs) {
296 | List