Skip to content

Commit 95c1a65

Browse files
committed
Replace retrofit2 with custom wrapper around OkHttpClient
1 parent 15bf374 commit 95c1a65

File tree

150 files changed

+6273
-2695
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+6273
-2695
lines changed

.github/dependabot.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,3 @@ updates:
88
open-pull-requests-limit: 10
99
reviewers:
1010
- sonallux
11-
ignore:
12-
- dependency-name: 'com.squareup.okhttp3:mockwebserver'
13-
versions: ['[4.0,)']
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package de.sonallux.spotify.generator.java;
2+
3+
import de.sonallux.spotify.core.model.SpotifyWebApiEndpoint;
4+
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.stream.Collectors;
8+
9+
import static de.sonallux.spotify.core.model.SpotifyWebApiEndpoint.ParameterLocation.*;
10+
11+
public class EndpointHelper {
12+
/**
13+
* Fixes duplicated endpoint parameters.
14+
* Some endpoints allow to pass data either via query argument or via body. As the url has a length limit,
15+
* passing to much data in the query string might result in an error response. Therefore this method removes
16+
* the option to pass the data via query argument and makes the body parameter mandatory.
17+
* @param endpoint the spotify api endpoint to fix
18+
*/
19+
public static void fixDuplicateEndpointParameters(SpotifyWebApiEndpoint endpoint) {
20+
var duplicates = endpoint.getParameters().stream()
21+
.collect(Collectors.groupingBy(SpotifyWebApiEndpoint.Parameter::getName))
22+
.entrySet().stream()
23+
.filter(e -> e.getValue().size() > 1)
24+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
25+
26+
duplicates.forEach((paramName, parameters) -> {
27+
if (!parameters.stream().map(SpotifyWebApiEndpoint.Parameter::getLocation).sorted().collect(Collectors.toList()).equals(List.of(QUERY, BODY))) {
28+
System.err.println("Endpoint " + endpoint.getName() + " has unfixable duplicate parameters");
29+
return;
30+
}
31+
endpoint.getParameters().removeIf(p -> p.getLocation() == QUERY && paramName.equals(p.getName()));
32+
for (var param : endpoint.getParameters()) {
33+
if (param.getLocation() == BODY && paramName.equals(param.getName())) {
34+
param.setRequired(true);
35+
} else if (param.getLocation() == HEADER && "Content-Type".equals(param.getName())) {
36+
param.setRequired(true);
37+
}
38+
}
39+
});
40+
}
41+
}

spotify-web-api-generator-java/src/main/java/de/sonallux/spotify/generator/java/EndpointRequestBodyHelper.java

Lines changed: 0 additions & 61 deletions
This file was deleted.

spotify-web-api-generator-java/src/main/java/de/sonallux/spotify/generator/java/EndpointRequestBodyObject.java

Lines changed: 0 additions & 32 deletions
This file was deleted.

spotify-web-api-generator-java/src/main/java/de/sonallux/spotify/generator/java/JavaGenerator.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,11 @@ public void generate(SpotifyWebApi spotifyWebApi, Path outputFolder, JavaPackage
3434
objectTemplate.generate(object, outputFolder, javaPackage);
3535
}
3636

37-
var apiTemplate = new CategoryTemplate().loadTemplate(this.mustacheFactory);
37+
var apiTemplate = new ApiTemplate().loadTemplate(this.mustacheFactory);
3838
for (var category : spotifyWebApi.getCategoryList()) {
3939
apiTemplate.generate(category, outputFolder, javaPackage);
4040
}
4141

42-
var requestBodyTemplate = new RequestBodyTemplate().loadTemplate(this.mustacheFactory);
43-
for (var object : EndpointRequestBodyHelper.getEndpointRequestBodies(spotifyWebApi)) {
44-
requestBodyTemplate.generate(object, outputFolder, javaPackage);
45-
}
46-
4742
new SpotifyWebApiTemplate()
4843
.loadTemplate(this.mustacheFactory)
4944
.generate(spotifyWebApi, outputFolder, javaPackage);
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package de.sonallux.spotify.generator.java.templates;
2+
3+
import com.github.mustachejava.Mustache;
4+
import com.github.mustachejava.MustacheFactory;
5+
import com.google.common.base.CaseFormat;
6+
import de.sonallux.spotify.core.model.SpotifyWebApiCategory;
7+
import de.sonallux.spotify.core.model.SpotifyWebApiEndpoint;
8+
import de.sonallux.spotify.generator.java.EndpointHelper;
9+
import de.sonallux.spotify.generator.java.util.JavaPackage;
10+
import de.sonallux.spotify.generator.java.util.JavaUtils;
11+
import de.sonallux.spotify.generator.java.util.Markdown2Html;
12+
import lombok.Getter;
13+
import lombok.Setter;
14+
15+
import java.io.IOException;
16+
import java.nio.file.Files;
17+
import java.nio.file.Path;
18+
import java.util.ArrayList;
19+
import java.util.HashMap;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.Stream;
24+
25+
import static java.nio.file.StandardOpenOption.*;
26+
import static java.util.stream.Collectors.joining;
27+
import static java.util.stream.Collectors.toList;
28+
29+
public class ApiTemplate {
30+
private Mustache apiTemplate;
31+
private Mustache requestTemplate;
32+
33+
private Path outputFolder;
34+
35+
public ApiTemplate loadTemplate(MustacheFactory mustacheFactory) {
36+
this.apiTemplate = mustacheFactory.compile("templates/api.mustache");
37+
this.requestTemplate = mustacheFactory.compile("templates/request.mustache");
38+
return this;
39+
}
40+
41+
public void generate(SpotifyWebApiCategory category, Path outputFolder, JavaPackage basePackage) throws IOException {
42+
this.outputFolder = outputFolder;
43+
44+
var apisJavaPackage = basePackage.child("apis");
45+
var className = JavaUtils.getClassName(category);
46+
var requestsJavaPackage = apisJavaPackage.child(className.replace("Api", "").toLowerCase());
47+
48+
var context = new HashMap<String, Object>();
49+
context.put("package", apisJavaPackage.getName());
50+
context.put("requestsPackage", requestsJavaPackage.getName());
51+
context.put("name", category.getName());
52+
context.put("className", className);
53+
context.put("documentationLink", category.getLink());
54+
var endpointContexts = new ArrayList<>();
55+
for (var endpoint : category.getEndpointList()) {
56+
endpointContexts.add(buildEndpointContext(endpoint, requestsJavaPackage));
57+
}
58+
context.put("endpoints", endpointContexts);
59+
60+
var packageFolder = getPackageFolder(outputFolder, apisJavaPackage);
61+
var outputFile = packageFolder.resolve(JavaUtils.getFileName(className));
62+
generateFile(outputFile, apiTemplate, context);
63+
}
64+
65+
private Path getPackageFolder(Path baseFolder, JavaPackage javaPackage) throws IOException {
66+
var packageFolder = baseFolder.resolve(javaPackage.getPath());
67+
Files.createDirectories(packageFolder);
68+
return packageFolder;
69+
}
70+
71+
private void generateFile(Path outputFile, Mustache mustacheTemplate, Object scope) throws IOException {
72+
try (var writer = Files.newBufferedWriter(outputFile, CREATE, TRUNCATE_EXISTING, WRITE)) {
73+
mustacheTemplate.execute(writer, scope);
74+
}
75+
}
76+
77+
private Map<String, Object> buildEndpointContext(SpotifyWebApiEndpoint endpoint, JavaPackage javaPackage) throws IOException {
78+
var requiredParameters = generateEndpointRequest(endpoint, javaPackage);
79+
80+
var context = new HashMap<String, Object>();
81+
var methodName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, JavaUtils.shrinkEndpointId(endpoint));
82+
context.put("methodName", methodName);
83+
context.put("name", endpoint.getName());
84+
context.put("description", Markdown2Html.convertToSingleLine(endpoint.getDescription()));
85+
context.put("requestBuilder", JavaUtils.getEndpointRequestBuilderName(endpoint));
86+
87+
context.put("requiredParameters", requiredParameters.stream()
88+
.map(Parameter::asMethodParameter)
89+
.collect(joining(", ")));
90+
context.put("javaDocParameters", requiredParameters.stream()
91+
.map(Parameter::asJavaDoc)
92+
.collect(toList()));
93+
94+
context.put("arguments", Stream.concat(Stream.of(new Parameter("apiClient", "ApiClient", "")), requiredParameters.stream())
95+
.map(Parameter::getJavaName)
96+
.collect(joining(", ")));
97+
98+
return context;
99+
}
100+
101+
private List<Parameter> generateEndpointRequest(SpotifyWebApiEndpoint endpoint, JavaPackage javaPackage) throws IOException {
102+
EndpointHelper.fixDuplicateEndpointParameters(endpoint);
103+
104+
var context = new HashMap<String, Object>();
105+
context.put("package", javaPackage.getName());
106+
context.put("name", endpoint.getName() + " request");
107+
context.put("documentationLink", endpoint.getLink());
108+
context.put("className", JavaUtils.getEndpointRequestBuilderName(endpoint));
109+
context.put("httpMethod", endpoint.getHttpMethod());
110+
context.put("path", endpoint.getPath());
111+
context.put("responseType", getResponseType(endpoint));
112+
context.put("responseDescription", Markdown2Html.convertToLines(endpoint.getResponseDescription()));
113+
if (endpoint.getNotes() != null) {
114+
context.put("hasNotes", true);
115+
context.put("notes", Markdown2Html.convertToLines(endpoint.getNotes()));
116+
}
117+
if (endpoint.getScopes().size() > 0) {
118+
context.put("scopes", String.join(", ", endpoint.getScopes()));
119+
}
120+
121+
var requiredPathParameters = new ArrayList<Parameter>();
122+
var requiredQueryParameters = new ArrayList<Parameter>();
123+
var requiredBodyParameters = new ArrayList<Parameter>();
124+
var optionalPathParameters = new ArrayList<Parameter>();
125+
var optionalQueryParameters = new ArrayList<Parameter>();
126+
var optionalBodyParameters = new ArrayList<Parameter>();
127+
128+
for (var parameter : endpoint.getParameters()) {
129+
switch (parameter.getLocation()) {
130+
case PATH: {
131+
addParameter(parameter, requiredPathParameters, optionalPathParameters);
132+
break;
133+
}
134+
case QUERY: {
135+
addParameter(parameter, requiredQueryParameters, optionalQueryParameters);
136+
break;
137+
}
138+
case BODY: {
139+
addParameter(parameter, requiredBodyParameters, optionalBodyParameters);
140+
break;
141+
}
142+
}
143+
}
144+
145+
context.put("requiredPathParameters", requiredPathParameters);
146+
context.put("requiredQueryParameters", requiredQueryParameters);
147+
context.put("requiredBodyParameters", requiredBodyParameters);
148+
context.put("optionalPathParameters", optionalPathParameters);
149+
context.put("optionalQueryParameters", optionalQueryParameters);
150+
context.put("optionalBodyParameters", optionalBodyParameters);
151+
152+
List<Parameter> requiredParameterList = new ArrayList<>();
153+
requiredParameterList.add(new Parameter("apiClient", "ApiClient", "The API client"));
154+
requiredParameterList.addAll(requiredPathParameters);
155+
requiredParameterList.addAll(requiredQueryParameters);
156+
requiredParameterList.addAll(requiredBodyParameters);
157+
context.put("requiredParameters", requiredParameterList.stream()
158+
.map(Parameter::asMethodParameter)
159+
.collect(joining(", ")));
160+
161+
context.put("requiredJavaDocParameters", requiredParameterList.stream().map(Parameter::asJavaDoc).collect(toList()));
162+
163+
var packageFolder = getPackageFolder(outputFolder, javaPackage);
164+
var outputFile = packageFolder.resolve(JavaUtils.getFileName(JavaUtils.getEndpointRequestBuilderName(endpoint)));
165+
generateFile(outputFile, requestTemplate, context);
166+
167+
return requiredParameterList.subList(1, requiredParameterList.size());
168+
}
169+
170+
private void addParameter(SpotifyWebApiEndpoint.Parameter parameter, List<Parameter> requiredParams, List<Parameter> optionalParams) {
171+
var mappedParameter = new Parameter(parameter.getName(), parameter.getType(), parameter.getDescription());
172+
if (parameter.isRequired()) {
173+
requiredParams.add(mappedParameter);
174+
} else {
175+
optionalParams.add(mappedParameter);
176+
}
177+
}
178+
179+
private String getResponseType(SpotifyWebApiEndpoint endpoint) {
180+
if (endpoint.getResponseTypes().stream()
181+
.map(SpotifyWebApiEndpoint.ResponseType::getType)
182+
.distinct().count() == 1) {
183+
return JavaUtils.mapToPrimitiveJavaType(endpoint.getResponseTypes().get(0).getType());
184+
}
185+
var nonVoidResponseTypes = endpoint.getResponseTypes().stream()
186+
.map(SpotifyWebApiEndpoint.ResponseType::getType).filter(t -> !"Void".equals(t)).distinct().collect(toList());
187+
if (nonVoidResponseTypes.size() == 1) {
188+
return JavaUtils.mapToPrimitiveJavaType(endpoint.getResponseTypes().get(0).getType());
189+
}
190+
return "";
191+
}
192+
193+
@Getter
194+
@Setter
195+
private static class Parameter {
196+
private String javaName;
197+
private String jsonName;
198+
private String type;
199+
private String description;
200+
201+
public Parameter(String jsonName, String type, String description) {
202+
this.javaName = JavaUtils.escapeFieldName(jsonName);
203+
this.jsonName = jsonName;
204+
this.type = JavaUtils.mapToPrimitiveJavaType(type);
205+
this.description = Markdown2Html.convertToSingleLine(description);
206+
}
207+
208+
public String asMethodParameter() {
209+
return type + " " + javaName;
210+
}
211+
public String asJavaDoc() {
212+
return "@param " + javaName + " " + description;
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)