Skip to content

Commit 8d88b82

Browse files
authored
[BUG] [Groovy] Fix client to work with services that support other formats in addition to JSON (#22908)
1 parent 2ee50ce commit 8d88b82

15 files changed

Lines changed: 209 additions & 75 deletions

File tree

bin/configs/groovy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ outputDir: samples/client/petstore/groovy
33
inputSpec: modules/openapi-generator/src/test/resources/3_0/groovy/petstore.yaml
44
templateDir: modules/openapi-generator/src/main/resources/Groovy
55
additionalProperties:
6+
enumPropertyNaming: "original"
67
hideGenerationTimestamp: "true"

modules/openapi-generator/src/main/resources/Groovy/ApiUtils.mustache

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package {{invokerPackage}}
22

3+
{{#withXml}}
4+
import java.io.StringReader
5+
import {{javaxPackage}}.xml.bind.JAXB
6+
{{/withXml}}
37
import groovy.json.JsonBuilder
48
import groovy.json.JsonGenerator
59
import groovyx.net.http.ChainedHttpConfig
610
import groovyx.net.http.ContentTypes
711
import groovyx.net.http.NativeHandlers
12+
import groovyx.net.http.FromServer
813
import groovyx.net.http.ToServer
914

1015
import static groovyx.net.http.HttpBuilder.configure
@@ -18,36 +23,59 @@ class ApiUtils {
1823
}
1924
.build()
2025

21-
void invokeApi(onSuccess, onFailure, basePath, versionPath, resourcePath, queryParams, headerParams, bodyParams, contentType, method, container, type) {
26+
void invokeApi(onSuccess, onFailure, basePath, versionPath, resourcePath, queryParams, headerParams, bodyParams, accept, contentType, method, container, type) {
2227
def (url, uriPath) = buildUrlAndUriPath(basePath, versionPath, resourcePath)
2328
println "url=$url uriPath=$uriPath"
2429
def http = configure {
2530
request.uri = url
2631
request.uri.path = uriPath
2732
request.encoder(ContentTypes.JSON, { final ChainedHttpConfig config, final ToServer ts ->
28-
final ChainedHttpConfig.ChainedRequest request = config.getChainedRequest();
33+
final ChainedHttpConfig.ChainedRequest request = config.getChainedRequest()
2934
if (NativeHandlers.Encoders.handleRawUpload(config, ts)) {
30-
return;
35+
return
3136
}
3237

33-
final Object body = NativeHandlers.Encoders.checkNull(request.actualBody());
38+
final Object body = NativeHandlers.Encoders.checkNull(request.actualBody())
3439
final String json = ((body instanceof String || body instanceof GString)
3540
? body.toString()
36-
: new JsonBuilder(body, jsonGenerator).toString());
37-
ts.toServer(NativeHandlers.Encoders.stringToStream(json, request.actualCharset()));
41+
: new JsonBuilder(body, jsonGenerator).toString())
42+
ts.toServer(NativeHandlers.Encoders.stringToStream(json, request.actualCharset()))
3843
})
44+
{{#withXml}}
45+
request.encoder(ContentTypes.XML, { final ChainedHttpConfig config, final ToServer ts ->
46+
final ChainedHttpConfig.ChainedRequest request = config.getChainedRequest()
47+
if (NativeHandlers.Encoders.handleRawUpload(config, ts)) {
48+
return
49+
}
50+
51+
final Object body = NativeHandlers.Encoders.checkNull(request.actualBody())
52+
String xml
53+
if (body instanceof String || body instanceof GString) {
54+
xml = body.toString()
55+
} else {
56+
StringWriter writer = new StringWriter()
57+
JAXB.marshal(body, writer)
58+
xml = writer.toString()
59+
}
60+
ts.toServer(NativeHandlers.Encoders.stringToStream(xml, request.actualCharset()))
61+
}){{/withXml}}
3962
}
4063
.invokeMethod(String.valueOf(method).toLowerCase()) {
4164
request.uri.query = queryParams
4265
request.headers = headerParams
4366
if (bodyParams != null) {
4467
request.body = bodyParams
4568
}
69+
request.accept = accept
4670
request.contentType = contentType
47-
48-
response.success { resp, json ->
71+
{{#withXml}}
72+
response.parser(ContentTypes.XML) { final ChainedHttpConfig cfg, final FromServer fs ->
73+
fs.inputStream.text
74+
}
75+
{{/withXml}}
76+
response.success { resp, body ->
4977
if (type != null) {
50-
onSuccess(parse(json, container, type))
78+
onSuccess(parse(resp, body, container, type))
5179
}
5280
}
5381
response.failure { resp ->
@@ -68,12 +96,24 @@ class ApiUtils {
6896
[basePath-pathOnly, pathOnly+versionPath+resourcePath]
6997
}
7098

71-
private def parse(object, container, clazz) {
99+
private def parse(response, object, container, clazz) {
100+
{{#withXml}}
101+
if (response.getContentType().toLowerCase().contains("xml")) {
102+
return JAXB.unmarshal(new StringReader(object), clazz)
103+
}
104+
{{/withXml}}
72105
if (container == "array") {
73-
return object.collect {parse(it, "", clazz)}
74-
} else {
106+
return object.collect { parse(response, it, "", clazz) }
107+
} else {
75108
return clazz.newInstance(object)
76109
}
77110
}
78111

112+
private def selectHeaderAccept(accepts) {
113+
def jsonMime = 'application/json'
114+
if (accepts.find { it.toLowerCase().startsWith(jsonMime) }) {
115+
return [jsonMime]
116+
}
117+
return accepts
118+
}
79119
}

modules/openapi-generator/src/main/resources/Groovy/api.mustache

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class {{classname}} {
1818
def queryParams = [:]
1919
def headerParams = [:]
2020
def bodyParams
21+
def accept
2122
def contentType
2223
2324
{{#allParams}}
@@ -71,7 +72,9 @@ class {{classname}} {
7172
{{/formParams}}
7273
{{/hasFormParams}}
7374

74-
apiUtils.invokeApi(onSuccess, onFailure, basePath, versionPath, resourcePath, queryParams, headerParams, bodyParams, contentType,
75+
accept = apiUtils.selectHeaderAccept([{{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}}])
76+
77+
apiUtils.invokeApi(onSuccess, onFailure, basePath, versionPath, resourcePath, queryParams, headerParams, bodyParams, accept, contentType,
7578
"{{httpMethod}}", "{{returnContainer}}",
7679
{{#returnBaseType}}{{{.}}}.class {{/returnBaseType}}{{^returnBaseType}}null {{/returnBaseType}})
7780

modules/openapi-generator/src/main/resources/Groovy/build.gradle.mustache

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
apply plugin: 'groovy'
22
apply plugin: 'idea'
33
apply plugin: 'eclipse'
4+
apply plugin: 'com.github.johnrengelman.shadow'
45

56
group = '{{groupId}}'
67
version = '{{artifactVersion}}'
@@ -18,6 +19,7 @@ buildscript {
1819
}
1920
dependencies {
2021
classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.24.20')
22+
classpath('com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:4.0.4')
2123
}
2224
}
2325

modules/openapi-generator/src/main/resources/Groovy/model.mustache

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import groovy.transform.Canonical
44
{{#imports}}
55
import {{import}};
66
{{/imports}}
7+
{{#withXml}}
8+
import {{javaxPackage}}.xml.bind.annotation.*;
9+
{{/withXml}}
710

811
{{#models}}
912
{{#model}}

modules/openapi-generator/src/main/resources/Groovy/modelClass.mustache

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
{{#withXml}}
2+
@XmlAccessorType(XmlAccessType.NONE)
3+
{{/withXml}}
14
@Canonical
25
class {{classname}} {
36
{{#vars}}
@@ -16,6 +19,12 @@ class {{classname}} {
1619

1720
{{/isEnum}}
1821
{{#description}}/* {{{.}}} */{{/description}}
22+
{{#withXml}}
23+
@Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
24+
{{#isXmlWrapped}}
25+
@XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
26+
{{/isXmlWrapped}}
27+
{{/withXml}}
1928
{{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}
2029
{{/vars}}
2130
}

modules/openapi-generator/src/main/resources/Groovy/modelEnum.mustache

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
{{#withXml}}
2+
@XmlEnum
3+
{{/withXml}}
14
enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} {
25
{{#allowableValues}}{{#enumVars}}
36
{{#enumDescription}}
47
/**
58
* {{.}}
69
*/
710
{{/enumDescription}}
11+
{{#withXml}}
12+
@XmlEnumValue({{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isLong}}"{{/isLong}}{{#isFloat}}"{{/isFloat}})
13+
{{/withXml}}
814
{{{name}}}({{{value}}}){{^-last}},
915
{{/-last}}{{/enumVars}}{{/allowableValues}}
1016

samples/client/petstore/groovy/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
apply plugin: 'groovy'
22
apply plugin: 'idea'
33
apply plugin: 'eclipse'
4+
apply plugin: 'com.github.johnrengelman.shadow'
45

56
group = 'org.openapitools'
67
version = '1.0.0'
@@ -18,6 +19,7 @@ buildscript {
1819
}
1920
dependencies {
2021
classpath(group: 'org.jfrog.buildinfo', name: 'build-info-extractor-gradle', version: '4.24.20')
22+
classpath('com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:4.0.4')
2123
}
2224
}
2325

samples/client/petstore/groovy/src/main/groovy/org/openapitools/api/ApiUtils.groovy

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import groovy.json.JsonGenerator
55
import groovyx.net.http.ChainedHttpConfig
66
import groovyx.net.http.ContentTypes
77
import groovyx.net.http.NativeHandlers
8+
import groovyx.net.http.FromServer
89
import groovyx.net.http.ToServer
910

1011
import static groovyx.net.http.HttpBuilder.configure
@@ -18,36 +19,37 @@ class ApiUtils {
1819
}
1920
.build()
2021

21-
void invokeApi(onSuccess, onFailure, basePath, versionPath, resourcePath, queryParams, headerParams, bodyParams, contentType, method, container, type) {
22+
void invokeApi(onSuccess, onFailure, basePath, versionPath, resourcePath, queryParams, headerParams, bodyParams, accept, contentType, method, container, type) {
2223
def (url, uriPath) = buildUrlAndUriPath(basePath, versionPath, resourcePath)
2324
println "url=$url uriPath=$uriPath"
2425
def http = configure {
2526
request.uri = url
2627
request.uri.path = uriPath
2728
request.encoder(ContentTypes.JSON, { final ChainedHttpConfig config, final ToServer ts ->
28-
final ChainedHttpConfig.ChainedRequest request = config.getChainedRequest();
29+
final ChainedHttpConfig.ChainedRequest request = config.getChainedRequest()
2930
if (NativeHandlers.Encoders.handleRawUpload(config, ts)) {
30-
return;
31+
return
3132
}
3233

33-
final Object body = NativeHandlers.Encoders.checkNull(request.actualBody());
34+
final Object body = NativeHandlers.Encoders.checkNull(request.actualBody())
3435
final String json = ((body instanceof String || body instanceof GString)
3536
? body.toString()
36-
: new JsonBuilder(body, jsonGenerator).toString());
37-
ts.toServer(NativeHandlers.Encoders.stringToStream(json, request.actualCharset()));
37+
: new JsonBuilder(body, jsonGenerator).toString())
38+
ts.toServer(NativeHandlers.Encoders.stringToStream(json, request.actualCharset()))
3839
})
40+
3941
}
4042
.invokeMethod(String.valueOf(method).toLowerCase()) {
4143
request.uri.query = queryParams
4244
request.headers = headerParams
4345
if (bodyParams != null) {
4446
request.body = bodyParams
4547
}
48+
request.accept = accept
4649
request.contentType = contentType
47-
48-
response.success { resp, json ->
50+
response.success { resp, body ->
4951
if (type != null) {
50-
onSuccess(parse(json, container, type))
52+
onSuccess(parse(resp, body, container, type))
5153
}
5254
}
5355
response.failure { resp ->
@@ -68,12 +70,19 @@ class ApiUtils {
6870
[basePath-pathOnly, pathOnly+versionPath+resourcePath]
6971
}
7072

71-
private def parse(object, container, clazz) {
73+
private def parse(response, object, container, clazz) {
7274
if (container == "array") {
73-
return object.collect {parse(it, "", clazz)}
74-
} else {
75+
return object.collect { parse(response, it, "", clazz) }
76+
} else {
7577
return clazz.newInstance(object)
7678
}
7779
}
7880

81+
private def selectHeaderAccept(accepts) {
82+
def jsonMime = 'application/json'
83+
if (accepts.find { it.toLowerCase().startsWith(jsonMime) }) {
84+
return [jsonMime]
85+
}
86+
return accepts
87+
}
7988
}

0 commit comments

Comments
 (0)