Skip to content

Commit 62ee62e

Browse files
committed
Merge branch 'master' of github.com:wordnik/swagger-codegen
2 parents f592414 + 8708303 commit 62ee62e

File tree

6 files changed

+193
-31
lines changed

6 files changed

+193
-31
lines changed

src/main/resources/csharp/api.mustache

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
// query params
4646
var queryParams = new Dictionary<String, String>();
4747
var headerParams = new Dictionary<String, String>();
48+
var formParams = new Dictionary<String, object>();
4849

4950
{{#requiredParamCount}}
5051
// verify required params are set
@@ -53,27 +54,41 @@
5354
}
5455
{{/requiredParamCount}}
5556

56-
string paramStr = null;
5757
{{#queryParams}}if ({{paramName}} != null){
58-
paramStr = ({{paramName}} != null && {{paramName}} is DateTime) ? ((DateTime)(object){{paramName}}).ToString("u") : Convert.ToString({{paramName}});
58+
string paramStr = ({{paramName}} is DateTime) ? ((DateTime)(object){{paramName}}).ToString("u") : Convert.ToString({{paramName}});
5959
queryParams.Add("{{paramName}}", paramStr);
6060
}
6161
{{/queryParams}}
6262

6363
{{#headerParams}}headerParams.Add("{{paramName}}", {{paramName}});
6464
{{/headerParams}}
6565

66-
try {
67-
var response = apiInvoker.invokeAPI(basePath, path, "{{httpMethod}}", queryParams, {{#bodyParam}}{{bodyParam}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, headerParams);
68-
if(response != null){
69-
return {{#returnType}}({{{returnType}}}) ApiInvoker.deserialize(response, typeof({{{returnType}}})){{/returnType}};
66+
{{#formParams}}if ({{paramName}} != null){
67+
if({{paramName}} is byte[]) {
68+
formParams.Add("{{paramName}}", {{paramName}});
69+
} else {
70+
string paramStr = ({{paramName}} is DateTime) ? ((DateTime)(object){{paramName}}).ToString("u") : Convert.ToString({{paramName}});
71+
formParams.Add("{{paramName}}", paramStr);
7072
}
71-
else {
72-
return {{#returnType}}null{{/returnType}};
73+
}
74+
{{/formParams}}
75+
76+
try {
77+
if (typeof({{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}) == typeof(byte[])) {
78+
var response = apiInvoker.invokeBinaryAPI(basePath, path, "GET", queryParams, null, headerParams, formParams);
79+
return {{#returnType}}((object)response) as {{{returnType}}}{{/returnType}};
80+
} else {
81+
var response = apiInvoker.invokeAPI(basePath, path, "{{httpMethod}}", queryParams, {{#bodyParam}}{{bodyParam}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, headerParams, formParams);
82+
if(response != null){
83+
return {{#returnType}}({{{returnType}}}) ApiInvoker.deserialize(response, typeof({{{returnType}}})){{/returnType}};
84+
}
85+
else {
86+
return {{#returnType}}null{{/returnType}};
87+
}
7388
}
7489
} catch (ApiException ex) {
7590
if(ex.ErrorCode == 404) {
76-
return {{#returnType}} null{{/returnType}};
91+
return {{#returnType}}null{{/returnType}};
7792
}
7893
else {
7994
throw ex;

src/main/resources/csharp/apiInvoker.mustache

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,17 @@
4444
}
4545
}
4646

47-
public string invokeAPI(string host, string path, string method, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams) {
47+
public string invokeAPI(string host, string path, string method, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams, Dictionary<String, object> formParams)
48+
{
49+
return invokeAPIInternal(host, path, method, false, queryParams, body, headerParams, formParams) as string;
50+
}
51+
52+
public byte[] invokeBinaryAPI(string host, string path, string method, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams, Dictionary<String, object> formParams)
53+
{
54+
return invokeAPIInternal(host, path, method, true, queryParams, body, headerParams, formParams) as byte[];
55+
}
56+
57+
private object invokeAPIInternal(string host, string path, string method, bool binaryResponse, Dictionary<String, String> queryParams, object body, Dictionary<String, String> headerParams, Dictionary<String, object> formParams) {
4858
var b = new StringBuilder();
4959
5060
foreach (var queryParamItem in queryParams)
@@ -60,9 +70,21 @@
6070
host = host.EndsWith("/") ? host.Substring(0, host.Length - 1) : host;
6171

6272
var client = WebRequest.Create(host + path + querystring);
63-
client.ContentType = "application/json";
6473
client.Method = method;
6574

75+
byte[] formData = null;
76+
if (formParams.Count > 0)
77+
{
78+
string formDataBoundary = String.Format("----------{0:N}", Guid.NewGuid());
79+
client.ContentType = "multipart/form-data; boundary=" + formDataBoundary;
80+
formData = GetMultipartFormData(formParams, formDataBoundary);
81+
client.ContentLength = formData.Length;
82+
}
83+
else
84+
{
85+
client.ContentType = "application/json";
86+
}
87+
6688
foreach (var headerParamsItem in headerParams)
6789
{
6890
client.Headers.Add(headerParamsItem.Key, headerParamsItem.Value);
@@ -79,9 +101,17 @@
79101
case "POST":
80102
case "PUT":
81103
case "DELETE":
82-
var swRequestWriter = new StreamWriter(client.GetRequestStream());
83-
swRequestWriter.Write(serialize(body));
84-
swRequestWriter.Close();
104+
using (Stream requestStream = client.GetRequestStream())
105+
{
106+
if (formData != null)
107+
{
108+
requestStream.Write(formData, 0, formData.Length);
109+
}
110+
111+
var swRequestWriter = new StreamWriter(requestStream);
112+
swRequestWriter.Write(serialize(body));
113+
swRequestWriter.Close();
114+
}
85115
break;
86116
default:
87117
throw new ApiException(500, "unknown method type " + method);
@@ -96,10 +126,22 @@
96126
throw new ApiException((int)webResponse.StatusCode, webResponse.StatusDescription);
97127
}
98128

99-
var responseReader = new StreamReader(webResponse.GetResponseStream());
100-
var responseData = responseReader.ReadToEnd();
101-
responseReader.Close();
102-
return responseData;
129+
if (binaryResponse)
130+
{
131+
using (var memoryStream = new MemoryStream())
132+
{
133+
webResponse.GetResponseStream().CopyTo(memoryStream);
134+
return memoryStream.ToArray();
135+
}
136+
}
137+
else
138+
{
139+
using (var responseReader = new StreamReader(webResponse.GetResponseStream()))
140+
{
141+
var responseData = responseReader.ReadToEnd();
142+
return responseData;
143+
}
144+
}
103145
}
104146
catch(WebException ex)
105147
{
@@ -113,5 +155,53 @@
113155
throw new ApiException(statusCode, ex.Message);
114156
}
115157
}
158+
159+
private static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
160+
{
161+
Stream formDataStream = new System.IO.MemoryStream();
162+
bool needsCLRF = false;
163+
164+
foreach (var param in postParameters)
165+
{
166+
// Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
167+
// Skip it on the first parameter, add it to subsequent parameters.
168+
if (needsCLRF)
169+
formDataStream.Write(Encoding.UTF8.GetBytes("\r\n"), 0, Encoding.UTF8.GetByteCount("\r\n"));
170+
171+
needsCLRF = true;
172+
173+
if (param.Value is byte[])
174+
{
175+
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n",
176+
boundary,
177+
param.Key,
178+
"application/octet-stream");
179+
formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, Encoding.UTF8.GetByteCount(postData));
180+
181+
// Write the file data directly to the Stream, rather than serializing it to a string.
182+
formDataStream.Write((param.Value as byte[]), 0, (param.Value as byte[]).Length);
183+
}
184+
else
185+
{
186+
string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}",
187+
boundary,
188+
param.Key,
189+
param.Value);
190+
formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, Encoding.UTF8.GetByteCount(postData));
191+
}
192+
}
193+
194+
// Add the end of the request. Start with a newline
195+
string footer = "\r\n--" + boundary + "--\r\n";
196+
formDataStream.Write(Encoding.UTF8.GetBytes(footer), 0, Encoding.UTF8.GetByteCount(footer));
197+
198+
// Dump the Stream into a byte[]
199+
formDataStream.Position = 0;
200+
byte[] formData = new byte[formDataStream.Length];
201+
formDataStream.Read(formData, 0, formData.Length);
202+
formDataStream.Close();
203+
204+
return formData;
205+
}
116206
}
117207
}

src/main/scala/com/wordnik/swagger/codegen/BasicCSharpGenerator.scala

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class BasicCSharpGenerator extends BasicGenerator {
4141
* We are using csharp objects instead of primitives to avoid showing default
4242
* primitive values when the API returns missing data. For instance, having a
4343
* {"count":0} != count is unknown. You can change this to use primitives if you
44-
* desire, but update the default values as well or they'll be set to null in
44+
* desire, but update the default values as well or they'll be set to null in
4545
* variable declarations.
4646
*/
4747
override def typeMapping = Map(
@@ -54,7 +54,9 @@ class BasicCSharpGenerator extends BasicGenerator {
5454
"double" -> "double?",
5555
"object" -> "object",
5656
"Date" -> "DateTime?",
57-
"date" -> "DateTime?")
57+
"date" -> "DateTime?",
58+
"File" -> "byte[]",
59+
"file" -> "byte[]")
5860

5961
// location of templates
6062
override def templateDir = "csharp"
@@ -70,11 +72,11 @@ class BasicCSharpGenerator extends BasicGenerator {
7072
// template used for models
7173
apiTemplateFiles += "api.mustache" -> ".cs"
7274

73-
override def reservedWords = Set("abstract", "continue", "for", "new", "switch", "assert",
74-
"default", "if", "package", "synchronized", "do", "goto", "private", "this", "break",
75-
"implements", "protected", "throw", "else", "import", "public", "throws", "case",
76-
"enum", "instanceof", "return", "transient", "catch", "extends", "try", "final",
77-
"interface", "static", "void", "class", "finally", "strictfp", "volatile", "const",
75+
override def reservedWords = Set("abstract", "continue", "for", "new", "switch", "assert",
76+
"default", "if", "package", "synchronized", "do", "goto", "private", "this", "break",
77+
"implements", "protected", "throw", "else", "import", "public", "throws", "case",
78+
"enum", "instanceof", "return", "transient", "catch", "extends", "try", "final",
79+
"interface", "static", "void", "class", "finally", "strictfp", "volatile", "const",
7880
"native", "super", "while")
7981

8082
// import/require statements for specific datatypes
@@ -180,7 +182,7 @@ class BasicCSharpGenerator extends BasicGenerator {
180182
}
181183

182184
override def escapeReservedWord(word: String) = {
183-
if (reservedWords.contains(word))
185+
if (reservedWords.contains(word))
184186
throw new Exception("reserved word " + "\"" + word + "\" not allowed")
185187
else word
186188
}
@@ -193,8 +195,8 @@ class BasicCSharpGenerator extends BasicGenerator {
193195
capitalize(name)
194196
}
195197
196-
def capitalize(s: String) = {
197-
s(0).toUpper + s.substring(1, s.length).toLowerCase
198+
def capitalize(s: String) = {
199+
s(0).toUpper + s.substring(1, s.length).toLowerCase
198200
}*/
199201

200202
// supporting classes

src/main/scala/com/wordnik/swagger/codegen/Codegen.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ class Codegen(config: CodegenConfig) {
309309
val ComplexTypeMatcher(basePart) = operation.responseClass
310310

311311
properties += "returnType" -> config.processResponseDeclaration(operation.responseClass.replaceAll(basePart, config.processResponseClass(basePart).get))
312-
properties += "returnContainer" -> (operation.responseClass.substring(0, n))
312+
properties += "returnContainer" -> config.processResponseClass(operation.responseClass.substring(0, n))
313313
properties += "returnBaseType" -> config.processResponseClass(basePart)
314314
properties += "returnTypeIsPrimitive" -> {
315315
(config.languageSpecificPrimitives.contains(basePart) || primitives.contains(basePart)) match {

src/test/scala/BasicScalaGeneratorTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers {
211211
m("returnType") should be (Some("List[Pet]"))
212212
m("returnTypeIsPrimitive") should be (None)
213213
m("pathParams").asInstanceOf[List[_]].size should be (0)
214-
m("returnContainer") should be ("List")
214+
m("returnContainer") should be (Some("List"))
215215
m("requiredParamCount") should be ("1")
216216

217217
val queryParams = m("queryParams").asInstanceOf[List[_]]
@@ -248,7 +248,7 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers {
248248
m("returnType") should be (Some("List[Pet]"))
249249
m("returnTypeIsPrimitive") should be (None)
250250
m("pathParams").asInstanceOf[List[_]].size should be (0)
251-
m("returnContainer") should be ("List")
251+
m("returnContainer") should be (Some("List"))
252252
m("requiredParamCount") should be ("1")
253253

254254
val queryParams = m("queryParams").asInstanceOf[List[_]]

src/test/scala/CodegenTest.scala

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Copyright 2014 Wordnik, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import com.wordnik.swagger.codegen.Codegen
18+
import com.wordnik.swagger.codegen.BasicJavaGenerator
19+
import com.wordnik.swagger.codegen.model._
20+
21+
import org.junit.runner.RunWith
22+
import org.scalatest.junit.JUnitRunner
23+
import org.scalatest.FlatSpec
24+
import org.scalatest.matchers.ShouldMatchers
25+
26+
import scala.reflect.BeanProperty
27+
28+
@RunWith(classOf[JUnitRunner])
29+
class CodegenTest extends FlatSpec with ShouldMatchers {
30+
31+
val subject = new Codegen(new BasicJavaGenerator)
32+
33+
val testOp = new Operation("GET",
34+
"List All Contacts",
35+
"",
36+
"Array[ContactData]",
37+
"listContacts",
38+
0,
39+
List.empty,
40+
List.empty,
41+
List.empty,
42+
List.empty,
43+
List.empty,
44+
List.empty,
45+
None)
46+
47+
behavior of "Codegen"
48+
/*
49+
* A return specified as "Array" should map to "List"
50+
*/
51+
it should "recognize the returnContainer as a List" in {
52+
val map = subject.apiToMap("/contacts", testOp)
53+
map("returnContainer") should be (Some("List"))
54+
}
55+
}

0 commit comments

Comments
 (0)