Skip to content

Commit 37cb9e7

Browse files
authored
modrinth: prioritize target loader when resolving project versions (#604)
1 parent b24de72 commit 37cb9e7

File tree

5 files changed

+157
-43
lines changed

5 files changed

+157
-43
lines changed

src/main/java/me/itzg/helpers/modrinth/ModrinthApiClient.java

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.nio.file.Files;
88
import java.nio.file.Path;
99
import java.util.ArrayList;
10+
import java.util.Collection;
1011
import java.util.Collections;
1112
import java.util.HashSet;
1213
import java.util.List;
@@ -181,31 +182,56 @@ public Mono<Path> downloadMrPack(VersionFile versionFile) {
181182
public Mono<List<Version>> getVersionsForProject(String projectIdOrSlug,
182183
@Nullable Loader loader, String gameVersion
183184
) {
184-
return sharedFetch.fetch(
185-
uriBuilder.resolve("/v2/project/{id|slug}/version",
186-
queryParameters()
187-
.addStringArray("loaders", expandCompatibleLoaders(loader))
188-
.addStringArray("game_versions", gameVersion),
189-
projectIdOrSlug
185+
return getJustVersionsForProject(projectIdOrSlug, gameVersion,
186+
loader != null ? Collections.singletonList(loader.toString()) : null,
187+
false
188+
)
189+
.switchIfEmpty(
190+
getJustVersionsForProject(projectIdOrSlug, gameVersion,
191+
expandCompatibleLoaders(loader),
192+
true
190193
)
191194
)
192-
.toObjectList(Version.class)
193-
.assemble()
194-
.flatMap(versions ->
195-
versions.isEmpty() ?
196-
getProject(projectIdOrSlug)
197-
.flatMap(project -> Mono.error(new NoFilesAvailableException(project, loader, gameVersion)))
198-
: Mono.just(versions)
199-
);
195+
.switchIfEmpty(
196+
getProject(projectIdOrSlug)
197+
.flatMap(project -> Mono.error(new NoFilesAvailableException(project, loader, gameVersion)))
198+
);
199+
}
200+
201+
/**
202+
* @return the non-empty list of versions or an empty mono
203+
*/
204+
private Mono<List<Version>> getJustVersionsForProject(
205+
String projectIdOrSlug, String gameVersion,
206+
Collection<String> loaderNames,
207+
boolean skipEmptyLoaders
208+
) {
209+
return
210+
skipEmptyLoaders && (loaderNames == null || loaderNames.isEmpty()) ?
211+
Mono.empty() :
212+
sharedFetch.fetch(
213+
uriBuilder.resolve("/v2/project/{id|slug}/version",
214+
queryParameters()
215+
.addStringArray("loaders", loaderNames)
216+
.addStringArray("game_versions", gameVersion),
217+
projectIdOrSlug
218+
)
219+
)
220+
.toObjectList(Version.class)
221+
.assemble()
222+
.filter(versions -> !versions.isEmpty());
200223
}
201224

225+
/**
226+
* @param loader the target loader
227+
* @return a list of the compatible loaders
228+
*/
202229
private List<String> expandCompatibleLoaders(@Nullable Loader loader) {
203230
if (loader == null) {
204231
return null;
205232
}
206233

207234
final ArrayList<String> expanded = new ArrayList<>();
208-
expanded.add(loader.toString());
209235
Loader compatibleWith = loader;
210236
while ((compatibleWith = compatibleWith.getCompatibleWith()) != null) {
211237
expanded.add(compatibleWith.toString());

src/test/java/me/itzg/helpers/modrinth/ModrinthApiClientTest.java

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,33 +45,75 @@ void getBulkProjectsWithUnknownServerSide(WireMockRuntimeInfo wmInfo) {
4545
}
4646
}
4747

48-
@Test
49-
void getVersionsForProject(WireMockRuntimeInfo wmInfo) {
50-
51-
stubFor(get(urlPathMatching("/v2/project/(BITzwT7B|clickvillagers)/version"))
52-
.withQueryParam("loaders", equalTo("[\"purpur\",\"paper\",\"spigot\"]"))
53-
.withQueryParam("game_versions", equalTo("[\"1.20.1\"]"))
54-
.willReturn(aResponse()
55-
.withHeader("Content-Type", "application/json")
56-
.withBodyFile("modrinth/project-BITzwT7B-version-resp.json")
57-
)
58-
);
48+
@Nested
49+
class getVersionsForProject {
50+
@Test
51+
void exactLoader(WireMockRuntimeInfo wmInfo) {
5952

60-
try (ModrinthApiClient client = new ModrinthApiClient(wmInfo.getHttpBaseUrl(), "modrinth", Options.builder().build())) {
61-
final List<Version> result = client.getVersionsForProject("BITzwT7B", Loader.purpur, "1.20.1")
62-
.block();
53+
stubFor(get(urlPathMatching("/v2/project/(BITzwT7B|clickvillagers)/version"))
54+
.withQueryParam("loaders", equalTo("[\"purpur\"]"))
55+
.withQueryParam("game_versions", equalTo("[\"1.20.1\"]"))
56+
.willReturn(aResponse()
57+
.withHeader("Content-Type", "application/json")
58+
.withBodyFile("modrinth/project-BITzwT7B-version-resp.json")
59+
)
60+
);
61+
62+
try (ModrinthApiClient client = new ModrinthApiClient(wmInfo.getHttpBaseUrl(), "modrinth", Options.builder().build())) {
63+
final List<Version> result = client.getVersionsForProject("BITzwT7B", Loader.purpur, "1.20.1")
64+
.block();
65+
66+
assertThat(result)
67+
.hasSize(3)
68+
.extracting(Version::getId)
69+
.containsExactly(
70+
"O9nndrTu",
71+
"DfUyEmsH",
72+
"oUJMLDhz"
73+
);
74+
}
75+
}
76+
77+
@Test
78+
void fallbackToCompatibleLoader(WireMockRuntimeInfo wmInfo) {
79+
stubFor(get(urlPathMatching("/v2/project/entityculling/version"))
80+
.withQueryParam("loaders", equalTo("[\"neoforge\"]"))
81+
.withQueryParam("game_versions", equalTo("[\"1.12.2\"]"))
82+
.willReturn(aResponse()
83+
.withHeader("Content-Type", "application/json")
84+
.withBodyFile("modrinth/versions-entityculling-neoforge-not-forge.json")
85+
)
86+
);
87+
stubFor(get(urlPathMatching("/v2/project/entityculling/version"))
88+
.withQueryParam("loaders", equalTo("[\"forge\"]"))
89+
.withQueryParam("game_versions", equalTo("[\"1.12.2\"]"))
90+
.willReturn(aResponse()
91+
.withHeader("Content-Type", "application/json")
92+
.withBodyFile("modrinth/versions-entityculling-forge.json")
93+
)
94+
);
6395

64-
assertThat(result)
65-
.hasSize(3)
66-
.extracting(Version::getId)
67-
.containsExactly(
68-
"O9nndrTu",
69-
"DfUyEmsH",
70-
"oUJMLDhz"
71-
);
96+
try (ModrinthApiClient client = new ModrinthApiClient(wmInfo.getHttpBaseUrl(), "modrinth",
97+
Options.builder().build()
98+
)) {
99+
final List<Version> result = client.getVersionsForProject(
100+
"entityculling",
101+
Loader.neoforge,
102+
"1.12.2"
103+
)
104+
.block();
105+
106+
assertThat(result)
107+
.extracting(Version::getId)
108+
.containsExactly(
109+
"knltv3Vh"
110+
);
111+
}
72112
}
73113
}
74114

115+
116+
75117
@Nested
76118
class resolveProjectVersion {
77119

@@ -142,7 +184,7 @@ void noFiles(WireMockRuntimeInfo wmInfo) {
142184
@Test
143185
void noApplicableVersionsOfType(WireMockRuntimeInfo wmInfo) {
144186
stubFor(get(urlPathMatching("/v2/project/(3wmN97b8|multiverse-core)/version"))
145-
.withQueryParam("loaders", equalTo("[\"purpur\",\"paper\",\"spigot\"]"))
187+
.withQueryParam("loaders", equalTo("[\"purpur\"]"))
146188
.withQueryParam("game_versions", equalTo("[\"1.21.1\"]"))
147189
.willReturn(aResponse()
148190
.withHeader("Content-Type", "application/json")

src/test/java/me/itzg/helpers/modrinth/ModrinthCommandTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
1515
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
1616
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder;
17-
import java.nio.file.Path;
1817
import java.nio.file.Files;
18+
import java.nio.file.Path;
1919
import java.util.Arrays;
2020
import java.util.function.Consumer;
2121
import me.itzg.helpers.LatchingExecutionExceptionHandler;
@@ -167,7 +167,8 @@ void failsWhenNoDependenciesForModLoader(@TempDir Path tempDir) throws JsonProce
167167
.put("project_id", requiredDepProjectId)
168168
.put("dependency_type", "required");
169169
});
170-
stubVersionRequestEmptyResponse(requiredDepProjectId);
170+
stubVersionRequestEmptyResponse(requiredDepProjectId, "paper");
171+
stubVersionRequestEmptyResponse(requiredDepProjectId, "spigot");
171172
stubGetProject(requiredDepProjectId, new Project().setProjectType(ProjectType.resourcepack));
172173

173174
stubDownload();
@@ -462,7 +463,7 @@ private void stubVersionRequest(String projectId, String versionId, Consumer<Arr
462463
depsAdder.accept(dependenciesArray);
463464

464465
stubFor(get(urlPathEqualTo("/v2/project/" + projectId + "/version"))
465-
.withQueryParam("loaders", equalTo("[\"paper\",\"spigot\"]"))
466+
.withQueryParam("loaders", equalTo("[\"paper\"]"))
466467
.withQueryParam("game_versions", equalTo("[\"1.21.1\"]"))
467468
.willReturn(aResponse()
468469
.withHeader("Content-Type", "application/json")
@@ -471,11 +472,11 @@ private void stubVersionRequest(String projectId, String versionId, Consumer<Arr
471472
);
472473
}
473474

474-
private void stubVersionRequestEmptyResponse(String projectId) {
475+
private void stubVersionRequestEmptyResponse(String projectId, String loader) {
475476
final ArrayNode versionResp = objectMapper.createArrayNode();
476477

477478
stubFor(get(urlPathEqualTo("/v2/project/" + projectId + "/version"))
478-
.withQueryParam("loaders", equalTo("[\"paper\",\"spigot\"]"))
479+
.withQueryParam("loaders", equalTo("[\"" + loader + "\"]"))
479480
.withQueryParam("game_versions", equalTo("[\"1.21.1\"]"))
480481
.willReturn(aResponse()
481482
.withHeader("Content-Type", "application/json")
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
[
2+
{
3+
"game_versions": [
4+
"1.12.2"
5+
],
6+
"loaders": [
7+
"forge"
8+
],
9+
"id": "knltv3Vh",
10+
"project_id": "NNAgCjsB",
11+
"author_id": "Qnt13hO8",
12+
"featured": false,
13+
"name": "1.6.3-1.12.2 - Forge",
14+
"version_number": "1.6.3-1.12.2",
15+
"changelog": "Initial backport to 1.12.2.",
16+
"changelog_url": null,
17+
"date_published": "2024-03-09T20:04:35.562295Z",
18+
"downloads": 293074,
19+
"version_type": "release",
20+
"status": "listed",
21+
"requested_status": null,
22+
"files": [
23+
{
24+
"hashes": {
25+
"sha512": "97c4def2a2979a0ba61c0fb016762756cf8361cdff651558dbb91cbe3d8627c388436d31ec2b93ff62df99a7ff589033184db4e6f298f4b8fdd4acf4c83ba202",
26+
"sha1": "fb22f03b297ca7b1bb7c6d5948e46c12e0a24446"
27+
},
28+
"url": "https://cdn.modrinth.com/data/NNAgCjsB/versions/knltv3Vh/entityculling-1.12.2-1.6.3.jar",
29+
"filename": "entityculling-1.12.2-1.6.3.jar",
30+
"primary": true,
31+
"size": 41478,
32+
"file_type": null
33+
}
34+
],
35+
"dependencies": [
36+
{
37+
"version_id": null,
38+
"project_id": "G1ckZuWK",
39+
"file_name": null,
40+
"dependency_type": "required"
41+
}
42+
]
43+
}
44+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]

0 commit comments

Comments
 (0)