Skip to content

Commit 75ebd9c

Browse files
authored
Add fix for missing body in upload playlist cover image (#93)
Fix #91
1 parent 50f5276 commit 75ebd9c

File tree

12 files changed

+568
-317
lines changed

12 files changed

+568
-317
lines changed

spotify-web-api-core/src/main/java/de/sonallux/spotify/core/EndpointHelper.java

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import de.sonallux.spotify.core.model.SpotifyWebApi;
44
import de.sonallux.spotify.core.model.SpotifyWebApiEndpoint;
55

6+
import java.util.ArrayList;
67
import java.util.List;
78
import java.util.Map;
9+
import java.util.Optional;
810
import java.util.stream.Collectors;
911

1012
import static de.sonallux.spotify.core.model.SpotifyWebApiEndpoint.ParameterLocation.*;
@@ -17,8 +19,8 @@ public static void splitEndpoints(SpotifyWebApi spotifyWebApi) throws IllegalArg
1719

1820
/**
1921
* Fixes duplicated endpoint parameters.
20-
* Some endpoints allow to pass data either via query argument or via body. As the url has a length limit,
21-
* passing to much data in the query string might result in an error response. Therefore this method removes
22+
* Some endpoints allow passing data either via query argument or via body. As the url has a length limit,
23+
* passing too much data in the query string might result in an error response. Therefore, this method removes
2224
* the option to pass the data via query argument and makes the body parameter mandatory.
2325
* @param spotifyWebApi the spotify web api documentation
2426
*/
@@ -30,33 +32,43 @@ public static void fixDuplicateEndpointParameters(SpotifyWebApi spotifyWebApi) {
3032

3133
/**
3234
* Fixes duplicated endpoint parameters.
33-
* Some endpoints allow to pass data either via query argument or via body. As the url has a length limit,
34-
* passing to much data in the query string might result in an error response. Therefore this method removes
35+
* Some endpoints allow passing data either via query argument or via body. As the url has a length limit,
36+
* passing too much data in the query string might result in an error response. Therefore, this method removes
3537
* the option to pass the data via query argument and makes the body parameter mandatory.
3638
* @param endpoint the spotify api endpoint to fix
3739
*/
3840
public static void fixDuplicateEndpointParameters(SpotifyWebApiEndpoint endpoint) {
39-
var duplicates = endpoint.getParameters().stream()
40-
.collect(Collectors.groupingBy(SpotifyWebApiEndpoint.Parameter::getName))
41-
.entrySet().stream()
42-
.filter(e -> e.getValue().size() > 1)
43-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
44-
45-
duplicates.forEach((paramName, parameters) -> {
46-
if (!parameters.stream().map(SpotifyWebApiEndpoint.Parameter::getLocation).sorted().collect(Collectors.toList()).equals(List.of(QUERY, BODY))) {
47-
System.err.println("Endpoint " + endpoint.getName() + " has unfixable duplicate parameters");
48-
return;
41+
if (endpoint.getRequestBody() == null || !(endpoint.getRequestBody() instanceof SpotifyWebApiEndpoint.JsonRequestBody)) {
42+
return;
43+
}
44+
var requestBody = ((SpotifyWebApiEndpoint.JsonRequestBody) endpoint.getRequestBody());
45+
46+
var iterator = endpoint.getParameters().iterator();
47+
while (iterator.hasNext()) {
48+
var parameter = iterator.next();
49+
var bodyParameter = getBodyParameter(requestBody, parameter.getName());
50+
if (bodyParameter.isEmpty()) {
51+
continue;
4952
}
50-
endpoint.getParameters().removeIf(p -> p.getLocation() == QUERY && paramName.equals(p.getName()));
51-
for (var param : endpoint.getParameters()) {
52-
if (param.getLocation() == BODY && paramName.equals(param.getName())) {
53-
if (!("endpoint-add-tracks-to-playlist".equals(endpoint.getId()) && "position".equals(param.getName()))) {
54-
param.setRequired(true);
55-
}
56-
} else if (param.getLocation() == HEADER && "Content-Type".equals(param.getName())) {
57-
param.setRequired(true);
58-
}
53+
54+
//Remove the query parameter
55+
iterator.remove();
56+
57+
// Parameter position in endpoint-add-tracks-to-playlist is optional
58+
if ("endpoint-add-tracks-to-playlist".equals(endpoint.getId()) && "position".equals(parameter.getName())) {
59+
continue;
5960
}
60-
});
61+
62+
// Mark body parameter and Content-Type header as required
63+
bodyParameter.get().setRequired(true);
64+
endpoint.getParameters().stream()
65+
.filter(p -> p.getLocation() == HEADER && "Content-Type".equals(p.getName()))
66+
.findFirst().ifPresent(p -> p.setRequired(true));
67+
68+
}
69+
}
70+
71+
private static Optional<SpotifyWebApiEndpoint.Parameter> getBodyParameter(SpotifyWebApiEndpoint.JsonRequestBody requestBody, String name) {
72+
return requestBody.getParameters().stream().filter(p -> p.getName().equals(name)).findFirst();
6173
}
6274
}

spotify-web-api-core/src/main/java/de/sonallux/spotify/core/EndpointSplitter.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public static void splitUsersTopArtistsAndTracksEndpoint(SpotifyWebApi apiDocume
4646
"GET",
4747
"/me/top/artists",
4848
parameters,
49+
null,
4950
responseDescriptionArtists,
5051
topArtistsAndTracks.getScopes(),
5152
topArtistsAndTracks.getNotes(),
@@ -59,6 +60,7 @@ public static void splitUsersTopArtistsAndTracksEndpoint(SpotifyWebApi apiDocume
5960
"GET",
6061
"/me/top/tracks",
6162
parameters,
63+
null,
6264
responseDescriptionTracks,
6365
topArtistsAndTracks.getScopes(),
6466
topArtistsAndTracks.getNotes(),
@@ -86,6 +88,8 @@ public static void splitReorderOrReplacePlaylistsTracksEndpoint(SpotifyWebApi ap
8688
var endpoint = category.getEndpoint("endpoint-reorder-or-replace-playlists-tracks")
8789
.orElseThrow(() -> new IllegalArgumentException("Can not find endpoint-reorder-or-replace-playlists-tracks"));
8890

91+
var requestBody = ((SpotifyWebApiEndpoint.JsonRequestBody) endpoint.getRequestBody());
92+
8993
var reorderParameterNames = List.of("Authorization", "Content-Type", "playlist_id", "range_start", "insert_before", "range_length", "snapshot_id");
9094
var replaceParameterNames = List.of("Authorization", "Content-Type", "playlist_id", "uris");
9195

@@ -105,14 +109,17 @@ public static void splitReorderOrReplacePlaylistsTracksEndpoint(SpotifyWebApi ap
105109
endpoint.getHttpMethod(),
106110
endpoint.getPath(),
107111
endpoint.getParameters().stream().filter(p -> reorderParameterNames.contains(p.getName())).collect(Collectors.toList()),
112+
new SpotifyWebApiEndpoint.JsonRequestBody("", requestBody.getParameters().stream().filter(p -> reorderParameterNames.contains(p.getName())).collect(Collectors.toList())),
108113
responseDescriptionParts[0] + "\n\n" + responseDescriptionParts[2],
109114
endpoint.getScopes(),
110115
endpoint.getNotes(),
111116
List.of(new SpotifyWebApiEndpoint.ResponseType("SnapshotIdObject", 200))
112117
);
113118

114-
reorderEndpoint.getParameters().stream().filter(p -> "range_start".equals(p.getName())).findFirst().get().setRequired(true);
115-
reorderEndpoint.getParameters().stream().filter(p -> "insert_before".equals(p.getName())).findFirst().get().setRequired(true);
119+
((SpotifyWebApiEndpoint.JsonRequestBody) reorderEndpoint.getRequestBody()).getParameters().stream()
120+
.filter(p -> "range_start".equals(p.getName())).findFirst().get().setRequired(true);
121+
((SpotifyWebApiEndpoint.JsonRequestBody) reorderEndpoint.getRequestBody()).getParameters().stream()
122+
.filter(p -> "insert_before".equals(p.getName())).findFirst().get().setRequired(true);
116123

117124
var replaceEndpoint = new SpotifyWebApiEndpoint(
118125
"endpoint-replace-playlists-tracks",
@@ -122,7 +129,8 @@ public static void splitReorderOrReplacePlaylistsTracksEndpoint(SpotifyWebApi ap
122129
endpoint.getHttpMethod(),
123130
endpoint.getPath(),
124131
endpoint.getParameters().stream().filter(p -> replaceParameterNames.contains(p.getName())).collect(Collectors.toList()),
125-
responseDescriptionParts[1] + "\n\n" + responseDescriptionParts[2],
132+
new SpotifyWebApiEndpoint.JsonRequestBody("", requestBody.getParameters().stream().filter(p -> replaceParameterNames.contains(p.getName())).collect(Collectors.toList())),
133+
responseDescriptionParts[1] + "\n\n" + responseDescriptionParts[2],
126134
endpoint.getScopes(),
127135
endpoint.getNotes(),
128136
List.of(new SpotifyWebApiEndpoint.ResponseType("SnapshotIdObject", 201))

spotify-web-api-core/src/main/java/de/sonallux/spotify/core/Yaml.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ private static ObjectMapper createYaml() {
2222
return new YAMLMapper()
2323
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
2424
.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
25+
.disable(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID)
2526
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
2627
}
2728
}

spotify-web-api-core/src/main/java/de/sonallux/spotify/core/model/SpotifyWebApiEndpoint.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package de.sonallux.spotify.core.model;
22

3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonSubTypes;
6+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
37
import lombok.*;
48

59
import java.util.ArrayList;
610
import java.util.List;
711

12+
import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.EXISTING_PROPERTY;
13+
814
@Getter
915
@Setter
1016
@NoArgsConstructor
@@ -22,13 +28,14 @@ public class SpotifyWebApiEndpoint {
2228
private String httpMethod;
2329
private String path;
2430
private List<Parameter> parameters;
31+
private RequestBody requestBody;
2532
private String responseDescription;
2633
private List<String> scopes;
2734
private String notes;
2835
private List<ResponseType> responseTypes;
2936

30-
public SpotifyWebApiEndpoint(@NonNull String id, String name, String link, String description, String httpMethod, String path, List<Parameter> parameters, String responseDescription, List<String> scopes, String notes) {
31-
this(id, name, link, description, httpMethod, path, parameters, responseDescription, scopes, notes, new ArrayList<>());
37+
public SpotifyWebApiEndpoint(@NonNull String id, String name, String link, String description, String httpMethod, String path, List<Parameter> parameters, RequestBody requestBody, String responseDescription, List<String> scopes, String notes) {
38+
this(id, name, link, description, httpMethod, path, parameters, requestBody, responseDescription, scopes, notes, new ArrayList<>());
3239
}
3340

3441
@Getter
@@ -58,6 +65,40 @@ public enum ParameterLocation {
5865
BODY
5966
}
6067

68+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "contentType", include = EXISTING_PROPERTY)
69+
@JsonSubTypes({
70+
@JsonSubTypes.Type(name = "application/json", value = JsonRequestBody.class),
71+
@JsonSubTypes.Type(name = "image/jpeg", value = Base64ImageRequestBody.class)
72+
})
73+
@Getter
74+
@Setter
75+
@AllArgsConstructor
76+
public abstract static class RequestBody {
77+
private String contentType;
78+
private String description;
79+
}
80+
81+
@Getter
82+
@Setter
83+
public static class JsonRequestBody extends RequestBody {
84+
private List<Parameter> parameters;
85+
86+
@JsonCreator
87+
public JsonRequestBody(@JsonProperty("description") String description, @JsonProperty("parameters") List<Parameter> parameters) {
88+
super("application/json", description);
89+
this.parameters = parameters;
90+
}
91+
}
92+
93+
@Getter
94+
@Setter
95+
public static class Base64ImageRequestBody extends RequestBody {
96+
@JsonCreator
97+
public Base64ImageRequestBody(@JsonProperty("description") String description) {
98+
super("image/jpeg", description);
99+
}
100+
}
101+
61102
@Getter
62103
@Setter
63104
@NoArgsConstructor

0 commit comments

Comments
 (0)