Skip to content

Commit 87a98a3

Browse files
authored
Merge pull request #14 from jamezrin/main
Add support for generating code for Spring RestClient
2 parents b6dd396 + 22ddd55 commit 87a98a3

24 files changed

+446
-1
lines changed

TARGETS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ Currently the following output targets are supported:
4040
| --------- | ------- | -------------------------------- |
4141
| `indent` | ` ` | line break & indent output value |
4242

43+
### [RestClient](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestClient.html)
44+
45+
> Spring Framework REST client
46+
47+
###### Options
48+
49+
| Option | Default | Description |
50+
| ------------ | -------- | -------------------------------- |
51+
| `indent` | ` ` | line break & indent output value |
52+
| `entityType` | `String` | Java type for the entity |
53+
4354
----
4455

4556
## JavaScript

src/targets/java/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ module.exports = {
1111
okhttp: require('./okhttp'),
1212
unirest: require('./unirest'),
1313
asynchttp: require('./asynchttp'),
14-
nethttp: require('./nethttp')
14+
nethttp: require('./nethttp'),
15+
restclient: require('./restclient')
1516
}

src/targets/java/restclient.js

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/**
2+
* @description
3+
* HTTP code snippet generator for Java using Spring RestClient.
4+
*
5+
* @author
6+
* @jamezrin
7+
*
8+
* for any questions or issues regarding the generated code snippet, please open an issue mentioning the author.
9+
*/
10+
11+
'use strict'
12+
13+
const CodeBuilder = require('../../helpers/code-builder')
14+
15+
const standardMethods = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'TRACE']
16+
17+
const standardMediaTypes = {
18+
'application/atom+xml': 'APPLICATION_ATOM_XML',
19+
'application/cbor': 'APPLICATION_CBOR',
20+
'application/x-www-form-urlencoded': 'APPLICATION_FORM_URLENCODED',
21+
'application/graphql-response+json': 'APPLICATION_GRAPHQL_RESPONSE',
22+
'application/json': 'APPLICATION_JSON',
23+
'application/x-ndjson': 'APPLICATION_NDJSON',
24+
'application/octet-stream': 'APPLICATION_OCTET_STREAM',
25+
'application/pdf': 'APPLICATION_PDF',
26+
'application/problem+json': 'APPLICATION_PROBLEM_JSON',
27+
'application/problem+xml': 'APPLICATION_PROBLEM_XML',
28+
'application/x-protobuf': 'APPLICATION_PROTOBUF',
29+
'application/rss+xml': 'APPLICATION_RSS_XML',
30+
'application/xhtml+xml': 'APPLICATION_XHTML_XML',
31+
'application/xml': 'APPLICATION_XML',
32+
'application/yaml': 'APPLICATION_YAML',
33+
'image/gif': 'IMAGE_GIF',
34+
'image/jpeg': 'IMAGE_JPEG',
35+
'image/png': 'IMAGE_PNG',
36+
'multipart/form-data': 'MULTIPART_FORM_DATA',
37+
'multipart/mixed': 'MULTIPART_MIXED',
38+
'multipart/related': 'MULTIPART_RELATED',
39+
'text/event-stream': 'TEXT_EVENT_STREAM',
40+
'text/html': 'TEXT_HTML',
41+
'text/markdown': 'TEXT_MARKDOWN',
42+
'text/plain': 'TEXT_PLAIN',
43+
'text/xml': 'TEXT_XML'
44+
}
45+
46+
const multipartMimeTypes = [
47+
'multipart/form-data',
48+
'multipart/mixed',
49+
'multipart/related',
50+
'multipart/alternative'
51+
]
52+
53+
module.exports = function (source, options) {
54+
const opts = Object.assign({
55+
indent: ' ',
56+
entityType: 'String'
57+
}, options)
58+
59+
const state = {
60+
bodyType: null
61+
}
62+
63+
const code = new CodeBuilder(opts.indent)
64+
65+
code.push('RestClient restClient = RestClient.create();')
66+
.blank()
67+
68+
if (source.postData) {
69+
if (source.postData.params && source.postData.mimeType === 'application/x-www-form-urlencoded') {
70+
state.bodyType = 'form'
71+
72+
code.push('MultiValueMap<String, String> formDataMap = new LinkedMultiValueMap<>();')
73+
74+
source.postData.params.forEach(function (param) {
75+
code.push('formDataMap.add("%qd", "%qd");', param.name, param.value)
76+
})
77+
78+
code.blank()
79+
} else if (source.postData.params && multipartMimeTypes.includes(source.postData.mimeType)) {
80+
state.bodyType = 'multipart'
81+
82+
code.push('MultipartBodyBuilder multipartBuilder = new MultipartBodyBuilder();')
83+
84+
source.postData.params.forEach(function (param) {
85+
if (param.fileName) {
86+
if (param.value) {
87+
code.push('multipartBuilder.part("%s", "%qd")', param.name, param.value)
88+
code.push(1, '.filename("%s")', param.fileName)
89+
} else {
90+
code.push('multipartBuilder.part("%s", new FileSystemResource("%s"))', param.name, param.fileName)
91+
}
92+
93+
if (param.contentType) {
94+
const mediaTypeConstant = standardMediaTypes[param.contentType]
95+
if (mediaTypeConstant) {
96+
code.push(1, '.contentType(MediaType.%s);', mediaTypeConstant)
97+
} else {
98+
code.push(1, '.contentType(MediaType.parseMediaType("%s"));', param.contentType)
99+
}
100+
} else {
101+
code.push(1, ';')
102+
}
103+
} else {
104+
code.push('multipartBuilder.part("%s", "%qd");', param.name, param.value || '')
105+
}
106+
})
107+
108+
code.blank()
109+
} else if (source.postData.text) {
110+
state.bodyType = 'plaintext'
111+
}
112+
}
113+
114+
code.push('ResponseEntity<%s> response = restClient', opts.entityType)
115+
116+
if (standardMethods.includes(source.method.toUpperCase())) {
117+
code.push(1, '.method(HttpMethod.%s)', source.method.toUpperCase())
118+
} else {
119+
code.push(1, '.method(HttpMethod.valueOf("%s"))', source.method.toUpperCase())
120+
}
121+
122+
if (Object.keys(source.queryObj).length) {
123+
code.push(1, '.uri("%s", uriBuilder -> {', source.url)
124+
125+
Object.keys(source.queryObj).forEach(function (key) {
126+
const value = source.queryObj[key]
127+
const iterable = Array.isArray(value) ? value : [value]
128+
iterable.forEach(function (val) {
129+
code.push(2, 'uriBuilder.queryParam("%qd", "%qd");', key, val)
130+
})
131+
})
132+
133+
code.push(2, 'return uriBuilder.build();')
134+
code.push(1, '})')
135+
} else {
136+
code.push(1, '.uri("%s")', source.url)
137+
}
138+
139+
if (source.cookies && source.cookies.length) {
140+
source.cookies.forEach(function (cookie) {
141+
code.push(1, '.cookie("%qd", "%qd")', cookie.name, cookie.value)
142+
})
143+
}
144+
145+
const headers = Object.keys(source.headersObj)
146+
if (headers.length) {
147+
headers.forEach(function (key) {
148+
if (key.toLowerCase() !== 'content-type') {
149+
code.push(1, '.header("%s", "%qd")', key, source.headersObj[key])
150+
}
151+
})
152+
}
153+
154+
if (source.postData && state.bodyType) {
155+
if (source.postData.mimeType) {
156+
const mediaTypeEnumConstant = standardMediaTypes[source.postData.mimeType]
157+
if (mediaTypeEnumConstant) {
158+
code.push(1, '.contentType(MediaType.%s)', mediaTypeEnumConstant)
159+
} else {
160+
code.push(1, '.contentType(MediaType.parseMediaType("%s"))', source.postData.mimeType)
161+
}
162+
}
163+
164+
if (state.bodyType === 'form') {
165+
code.push(1, '.body(formDataMap)')
166+
} else if (state.bodyType === 'multipart') {
167+
code.push(1, '.body(multipartBuilder.build())')
168+
} else if (state.bodyType === 'plaintext') {
169+
code.push(1, '.body("%qd")', source.postData.text)
170+
}
171+
}
172+
173+
code.push(1, '.retrieve()')
174+
code.push(1, '.toEntity(%s.class);', opts.entityType)
175+
176+
return code.join()
177+
}
178+
179+
module.exports.info = {
180+
key: 'restclient',
181+
title: 'Spring RestClient',
182+
link: 'https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestClient.html',
183+
description: 'Spring Framework REST client'
184+
}

test/fixtures/available-targets.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,12 @@
226226
"title": "java.net.http",
227227
"link": "https://openjdk.java.net/groups/net/httpclient/intro.html",
228228
"description": "Java Standardized HTTP Client API"
229+
},
230+
{
231+
"key": "restclient",
232+
"title": "Spring RestClient",
233+
"link": "https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestClient.html",
234+
"description": "Spring Framework REST client"
229235
}
230236
]
231237
},
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
RestClient restClient = RestClient.create();
2+
3+
MultiValueMap<String, String> formDataMap = new LinkedMultiValueMap<>();
4+
formDataMap.add("foo", "bar");
5+
formDataMap.add("hello", "world");
6+
7+
ResponseEntity<String> response = restClient
8+
.method(HttpMethod.POST)
9+
.uri("http://mockbin.com/har")
10+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
11+
.body(formDataMap)
12+
.retrieve()
13+
.toEntity(String.class);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
RestClient restClient = RestClient.create();
2+
3+
ResponseEntity<String> response = restClient
4+
.method(HttpMethod.POST)
5+
.uri("http://mockbin.com/har")
6+
.contentType(MediaType.APPLICATION_JSON)
7+
.body("{\"number\":1,\"string\":\"f\\\"oo\",\"arr\":[1,2,3],\"nested\":{\"a\":\"b\"},\"arr_mix\":[1,\"a\",{\"arr_mix_nested\":{}}],\"boolean\":false}")
8+
.retrieve()
9+
.toEntity(String.class);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
RestClient restClient = RestClient.create();
2+
3+
ResponseEntity<String> response = restClient
4+
.method(HttpMethod.GET)
5+
.uri("http://mockbin.com/har")
6+
.header("accept-encoding", "deflate, gzip, br")
7+
.retrieve()
8+
.toEntity(String.class);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
RestClient restClient = RestClient.create();
2+
3+
ResponseEntity<String> response = restClient
4+
.method(HttpMethod.POST)
5+
.uri("http://mockbin.com/har")
6+
.cookie("foo", "bar")
7+
.cookie("bar", "baz")
8+
.retrieve()
9+
.toEntity(String.class);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
RestClient restClient = RestClient.create();
2+
3+
ResponseEntity<String> response = restClient
4+
.method(HttpMethod.valueOf("PROPFIND"))
5+
.uri("http://mockbin.com/har")
6+
.retrieve()
7+
.toEntity(String.class);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
RestClient restClient = RestClient.create();
2+
3+
MultiValueMap<String, String> formDataMap = new LinkedMultiValueMap<>();
4+
formDataMap.add("foo", "bar");
5+
6+
ResponseEntity<String> response = restClient
7+
.method(HttpMethod.POST)
8+
.uri("http://mockbin.com/har", uriBuilder -> {
9+
uriBuilder.queryParam("foo", "bar");
10+
uriBuilder.queryParam("foo", "baz");
11+
uriBuilder.queryParam("baz", "abc");
12+
uriBuilder.queryParam("key", "value");
13+
return uriBuilder.build();
14+
})
15+
.cookie("foo", "bar")
16+
.cookie("bar", "baz")
17+
.header("accept", "application/json")
18+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
19+
.body(formDataMap)
20+
.retrieve()
21+
.toEntity(String.class);

0 commit comments

Comments
 (0)