Skip to content

Commit 3254549

Browse files
authored
Merge pull request #28 from hauner/#24
resolves #24
2 parents 85af4ab + 994fcbc commit 3254549

File tree

15 files changed

+303
-6
lines changed

15 files changed

+303
-6
lines changed

src/main/groovy/com/github/hauner/openapi/spring/converter/ApiConverter.groovy

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.github.hauner.openapi.spring.converter
1818

1919
import com.github.hauner.openapi.spring.model.Api
2020
import com.github.hauner.openapi.spring.model.Endpoint
21+
import com.github.hauner.openapi.spring.model.RequestBody
2122
import com.github.hauner.openapi.spring.model.parameters.CookieParameter
2223
import com.github.hauner.openapi.spring.model.parameters.HeaderParameter
2324
import com.github.hauner.openapi.spring.model.parameters.Parameter as ModelParameter
@@ -88,6 +89,26 @@ class ApiConverter {
8889
ep.parameters.addAll (createParameter(parameter, target, resolver))
8990
}
9091

92+
if (httpOperation.requestBody != null) {
93+
def required = httpOperation.requestBody.required != null ?: false
94+
httpOperation.requestBody.content.each { Map.Entry<String, MediaType> requestBodyEntry ->
95+
def contentType = requestBodyEntry.key
96+
def requestBody = requestBodyEntry.value
97+
98+
def info = new SchemaInfo (requestBody.schema, getInlineTypeName (path))
99+
info.resolver = resolver
100+
101+
DataType dataType = dataTypeConverter.convert (info, target.models)
102+
103+
def body = new RequestBody(
104+
contentType: contentType,
105+
requestBodyType: dataType,
106+
required: required)
107+
108+
ep.requestBodies.add (body)
109+
}
110+
}
111+
91112
httpOperation.responses.each { Map.Entry<String, ApiResponse> responseEntry ->
92113
def httpStatus = responseEntry.key
93114
def httpResponse = responseEntry.value
@@ -136,6 +157,10 @@ class ApiConverter {
136157
}
137158
}
138159

160+
private String getInlineTypeName (String path) {
161+
StringUtil.toCamelCase (path.substring (1)) + 'RequestBody'
162+
}
163+
139164
private String getInlineResponseName (String path, String httpStatus) {
140165
StringUtil.toCamelCase (path.substring (1)) + 'Response' + httpStatus
141166
}
@@ -189,4 +214,5 @@ class ApiConverter {
189214
private boolean hasTags (op) {
190215
op.tags && !op.tags.empty
191216
}
217+
192218
}

src/main/groovy/com/github/hauner/openapi/spring/model/Endpoint.groovy

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ class Endpoint {
2727
String path
2828
HttpMethod method
2929

30-
List<Response> responses = []
3130
List<Parameter> parameters = []
31+
List<RequestBody> requestBodies = []
32+
List<Response> responses = []
33+
34+
RequestBody getRequestBody () {
35+
requestBodies.first ()
36+
}
3237

3338
Response getResponse () {
3439
responses.first ()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2019 the original authors
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+
package com.github.hauner.openapi.spring.model
18+
19+
import com.github.hauner.openapi.spring.model.datatypes.DataType
20+
21+
/**
22+
* Endpoint request body properties.
23+
*
24+
* @author Martin Hauner
25+
*/
26+
class RequestBody {
27+
28+
String contentType
29+
DataType requestBodyType
30+
boolean required
31+
32+
Set<String> getImports () {
33+
requestBodyType.imports
34+
}
35+
36+
String getAnnotationName () {
37+
"RequestBody"
38+
}
39+
40+
String getAnnotationWithPackage () {
41+
"org.springframework.web.bind.annotation.${annotationName}"
42+
}
43+
44+
String getAnnotation () {
45+
"@${annotationName}"
46+
}
47+
48+
}

src/main/groovy/com/github/hauner/openapi/spring/model/Response.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import com.github.hauner.openapi.spring.model.datatypes.DataType
2020
import com.github.hauner.openapi.spring.model.datatypes.NoneDataType
2121

2222
/**
23-
* Endpoint response properties,
23+
* Endpoint response properties.
2424
*
2525
* @author Martin Hauner
2626
*/

src/main/groovy/com/github/hauner/openapi/spring/writer/MethodWriter.groovy

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.github.hauner.openapi.spring.writer
1818

1919
import com.github.hauner.openapi.spring.model.Endpoint
20+
import com.github.hauner.openapi.spring.model.RequestBody
2021
import com.github.hauner.openapi.spring.model.parameters.Parameter
2122
import com.github.hauner.openapi.support.Identifier
2223

@@ -39,6 +40,11 @@ class MethodWriter {
3940
mapping += "("
4041
mapping += 'path = ' + quote(endpoint.path)
4142

43+
if (!endpoint.requestBodies.empty) {
44+
mapping += ", "
45+
mapping += 'consumes = {' + quote(endpoint.requestBody.contentType) + '}'
46+
}
47+
4248
if (!endpoint.response.empty) {
4349
mapping += ", "
4450
mapping += 'produces = {' + quote(endpoint.response.contentType) + '}'
@@ -68,6 +74,17 @@ class MethodWriter {
6874
param
6975
}
7076

77+
private String createRequestBodyAnnotation (RequestBody requestBody) {
78+
String param = "${requestBody.annotation}"
79+
80+
// required is default, so add required only if the parameter is not required
81+
if (!requestBody.required) {
82+
param += '(required = false)'
83+
}
84+
85+
param
86+
}
87+
7188
private String createMethodName (Endpoint endpoint) {
7289
def tokens = endpoint.path.tokenize ('/')
7390
tokens = tokens.collect { Identifier.fromJson (it).capitalize () }
@@ -86,6 +103,12 @@ class MethodWriter {
86103

87104
}
88105

106+
if (!endpoint.requestBodies.empty) {
107+
def body = endpoint.requestBody
108+
def param = "${createRequestBodyAnnotation(body)} ${body.requestBodyType.name} body"
109+
ps.add (param)
110+
}
111+
89112
ps.join (', ')
90113
}
91114

src/test/groovy/com/github/hauner/openapi/spring/converter/ApiConverterParameterSpec.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,5 @@ paths:
217217
e.name == 'foo'
218218
e.type == 'unknown'
219219
}
220+
220221
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2019 the original authors
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+
package com.github.hauner.openapi.spring.converter
18+
19+
import spock.lang.Specification
20+
21+
import static com.github.hauner.openapi.spring.support.OpenApiParser.parse
22+
23+
class ApiConverterRequestBodySpec extends Specification {
24+
25+
void "converts request body parameter"() {
26+
def openApi = parse (
27+
"""\
28+
openapi: 3.0.2
29+
info:
30+
title: test request body parameter
31+
version: 1.0.0
32+
33+
paths:
34+
/endpoint:
35+
36+
get:
37+
tags:
38+
- endpoint
39+
requestBody:
40+
content:
41+
application/json:
42+
schema:
43+
type: object
44+
properties:
45+
foo:
46+
type: string
47+
responses:
48+
'204':
49+
description: empty
50+
""")
51+
52+
when:
53+
def api = new ApiConverter ().convert (openApi)
54+
55+
then:
56+
def itf = api.interfaces.first ()
57+
def ep = itf.endpoints.first ()
58+
def body = ep.requestBodies.first ()
59+
body.contentType == 'application/json'
60+
body.requestBodyType.type == 'EndpointRequestBody'
61+
!body.required
62+
body.annotation == '@RequestBody'
63+
body.annotationWithPackage == 'org.springframework.web.bind.annotation.RequestBody'
64+
}
65+
66+
}

src/test/groovy/com/github/hauner/openapi/spring/writer/MethodWriterSpec.groovy

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.github.hauner.openapi.spring.writer
1818

1919
import com.github.hauner.openapi.spring.model.Endpoint
2020
import com.github.hauner.openapi.spring.model.HttpMethod
21+
import com.github.hauner.openapi.spring.model.RequestBody
2122
import com.github.hauner.openapi.spring.model.Response
2223
import com.github.hauner.openapi.spring.model.datatypes.BooleanDataType
2324
import com.github.hauner.openapi.spring.model.datatypes.CollectionDataType
@@ -322,4 +323,47 @@ class MethodWriterSpec extends Specification {
322323
"""
323324
}
324325
326+
void "writes required request body parameter" () {
327+
def endpoint = new Endpoint (path: '/foo', method: HttpMethod.POST, responses: [
328+
new Response (contentType: 'application/json', responseType: new NoneDataType())
329+
], requestBodies: [
330+
new RequestBody(
331+
contentType: 'application/json',
332+
requestBodyType: new ObjectDataType (type: 'FooRequestBody',
333+
properties: ['foo': new StringDataType ()] as LinkedHashMap),
334+
required: true)
335+
])
336+
337+
when:
338+
writer.write (target, endpoint)
339+
340+
then:
341+
target.toString () == """\
342+
@PostMapping(path = "${endpoint.path}", consumes = {"application/json"})
343+
ResponseEntity<void> postFoo(@RequestBody FooRequestBody body);
344+
"""
345+
}
346+
347+
void "writes optional request body parameter" () {
348+
def endpoint = new Endpoint (path: '/foo', method: HttpMethod.POST, responses: [
349+
new Response (contentType: 'application/json', responseType: new NoneDataType())
350+
], requestBodies: [
351+
new RequestBody(
352+
contentType: 'application/json',
353+
requestBodyType: new ObjectDataType (
354+
type: 'FooRequestBody',
355+
properties: ['foo': new StringDataType ()] as LinkedHashMap),
356+
required: false)
357+
])
358+
359+
when:
360+
writer.write (target, endpoint)
361+
362+
then:
363+
target.toString () == """\
364+
@PostMapping(path = "${endpoint.path}", consumes = {"application/json"})
365+
ResponseEntity<void> postFoo(@RequestBody(required = false) FooRequestBody body);
366+
"""
367+
}
368+
325369
}

src/testInt/groovy/com/github/hauner/openapi/generatr/GeneratrEndToEndTest.groovy

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package com.github.hauner.openapi.generatr
1818

1919
import org.junit.runner.RunWith
20-
import org.junit.runners.Parameterized;
20+
import org.junit.runners.Parameterized
2121

2222

2323
/**
@@ -30,12 +30,14 @@ class GeneratrEndToEndTest extends GeneratrTestBase {
3030
@Parameterized.Parameters(name = "{0}")
3131
static Collection<TestSet> sources () {
3232
return [
33+
new TestSet(name: 'ref-into-another-file'),
3334
new TestSet(name: 'no-response-content'),
3435
new TestSet(name: 'response-simple-data-types'),
3536
new TestSet(name: 'response-complex-data-types'),
36-
new TestSet(name: 'ref-into-another-file'),
37+
new TestSet(name: 'response-array-data-type-mapping'),
3738
new TestSet(name: 'params-simple-data-types'),
38-
new TestSet(name: 'response-array-data-type-mapping')
39+
new TestSet(name: 'params-path-simple-data-types'),
40+
new TestSet(name: 'params-request-body')
3941
]
4042
}
4143

src/testInt/groovy/com/github/hauner/openapi/generatr/GeneratrPendingTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class GeneratrPendingTest extends GeneratrTestBase {
2727
@Parameterized.Parameters(name = "{0}")
2828
static Collection<TestSet> sources () {
2929
return [
30-
new TestSet(name: 'path-params-simple-data-types')
30+
new TestSet(name: 'params-request-body')
3131
]
3232
}
3333

0 commit comments

Comments
 (0)