Skip to content

Commit a23a5d6

Browse files
authored
modrinth: more info in error when dependency has no applicable files (#489)
1 parent 57e9cb5 commit a23a5d6

File tree

5 files changed

+136
-21
lines changed

5 files changed

+136
-21
lines changed

src/main/java/me/itzg/helpers/McImageHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import me.itzg.helpers.singles.Asciify;
3434
import me.itzg.helpers.singles.HashCommand;
3535
import me.itzg.helpers.singles.NetworkInterfacesCommand;
36+
import me.itzg.helpers.singles.TestLoggingCommand;
3637
import me.itzg.helpers.singles.YamlPathCmd;
3738
import me.itzg.helpers.sync.InterpolateCommand;
3839
import me.itzg.helpers.sync.MulitCopyCommand;
@@ -82,6 +83,7 @@
8283
SetPropertiesCommand.class,
8384
Sync.class,
8485
SyncAndInterpolate.class,
86+
TestLoggingCommand.class,
8587
YamlPathCmd.class,
8688
VanillaTweaksCommand.class,
8789
}

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

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ public class ModrinthCommand implements Callable<Integer> {
6363
description = "Default is ${DEFAULT-VALUE}\nValid values: ${COMPLETION-CANDIDATES}")
6464
DownloadDependencies downloadDependencies;
6565

66+
@Option(names = "--skip-existing", defaultValue = "${env:MODRINTH_SKIP_EXISTING}")
67+
boolean skipExisting = true;
68+
69+
@Option(names = "--skip-up-to-date", defaultValue = "${env:MODRINTH_SKIP_UP_TO_DATE}")
70+
boolean skipUpToDate = true;
71+
6672
public enum DownloadDependencies {
6773
NONE,
6874
REQUIRED,
@@ -151,30 +157,43 @@ private ModrinthManifest loadManifest() throws IOException {
151157
return Manifests.load(outputDirectory, ModrinthManifest.ID, ModrinthManifest.class);
152158
}
153159

154-
private Stream<Version> expandDependencies(ModrinthApiClient modrinthApiClient, Version version) {
160+
private Stream<Version> expandDependencies(ModrinthApiClient modrinthApiClient, Project project, Version version) {
155161
log.debug("Expanding dependencies of version={}", version);
156162
return version.getDependencies().stream()
157163
.filter(this::filterDependency)
158164
.filter(dep -> projectsProcessed.add(dep.getProjectId()))
159165
.flatMap(dep -> {
160166
projectsProcessed.add(dep.getProjectId());
167+
161168
final Version depVersion;
162-
if (dep.getVersionId() == null) {
163-
log.debug("Fetching versions of dep={} and picking", dep);
164-
depVersion = pickVersion(
165-
getVersionsForProject(modrinthApiClient, dep.getProjectId())
169+
try {
170+
if (dep.getVersionId() == null) {
171+
log.debug("Fetching versions of dep={} and picking", dep);
172+
depVersion = pickVersion(
173+
getVersionsForProject(modrinthApiClient, dep.getProjectId())
174+
);
175+
}
176+
else {
177+
log.debug("Fetching version for dep={}", dep);
178+
depVersion = modrinthApiClient.getVersionFromId(dep.getVersionId())
179+
.block();
180+
}
181+
} catch (GenericException e) {
182+
throw new GenericException(String.format("Failed to expand %s of project '%s'",
183+
dep, project.getTitle()), e);
184+
} catch (NoFilesAvailableException e) {
185+
throw new InvalidParameterException(
186+
String.format("No matching files for %s of project '%s': %s",
187+
dep, project.getTitle(), e.getMessage()
188+
), e
166189
);
167190
}
168-
else {
169-
log.debug("Fetching version for dep={}", dep);
170-
depVersion = modrinthApiClient.getVersionFromId(dep.getVersionId())
171-
.block();
172-
}
191+
173192
if (depVersion != null) {
174193
log.debug("Resolved version={} for dep={}", depVersion.getVersionNumber(), dep);
175194
return Stream.concat(
176195
Stream.of(depVersion),
177-
expandDependencies(modrinthApiClient, depVersion)
196+
expandDependencies(modrinthApiClient, project, depVersion)
178197
)
179198
.peek(expandedVer -> log.debug("Expanded dependency={} into version={}", dep, expandedVer));
180199
}
@@ -243,7 +262,8 @@ private Path download(boolean isDatapack, VersionFile versionFile) {
243262
return fetch(URI.create(versionFile.getUrl()))
244263
.userAgentCommand("modrinth")
245264
.toFile(outPath)
246-
.skipUpToDate(true)
265+
.skipExisting(skipExisting)
266+
.skipUpToDate(skipUpToDate)
247267
.handleStatus(Fetch.loggingDownloadStatusHandler(log))
248268
.execute();
249269
} catch (IOException e) {
@@ -293,7 +313,7 @@ private Stream<Path> processProject(ModrinthApiClient modrinthApiClient, Project
293313

294314
return Stream.concat(
295315
Stream.of(version),
296-
expandDependencies(modrinthApiClient, version)
316+
expandDependencies(modrinthApiClient, project, version)
297317
)
298318
.map(ModrinthApiClient::pickVersionFile)
299319
.map(versionFile -> download(isDatapack, versionFile))
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package me.itzg.helpers.modrinth.model;
22

33
public enum ProjectType {
4-
mod,
5-
modpack
4+
mod,
5+
modpack,
6+
resourcepack,
67
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package me.itzg.helpers.singles;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import picocli.CommandLine.Command;
5+
import picocli.CommandLine.ExitCode;
6+
7+
import java.util.concurrent.Callable;
8+
9+
@Command(name = "test-logging-levels")
10+
@Slf4j
11+
public class TestLoggingCommand implements Callable<Integer> {
12+
13+
@Override
14+
public Integer call() throws Exception {
15+
log.error("This is an error");
16+
log.warn("This is a warning");
17+
log.info("This is an info");
18+
log.debug("This is a debug");
19+
return ExitCode.OK;
20+
}
21+
}

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

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
77
import static org.assertj.core.api.Assertions.assertThat;
88

9+
import com.fasterxml.jackson.core.JsonProcessingException;
910
import com.fasterxml.jackson.databind.ObjectMapper;
1011
import com.fasterxml.jackson.databind.node.ArrayNode;
1112
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -14,8 +15,12 @@
1415
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder;
1516
import java.nio.file.Path;
1617
import java.util.function.Consumer;
18+
import me.itzg.helpers.LatchingExecutionExceptionHandler;
19+
import me.itzg.helpers.errors.InvalidParameterException;
1720
import me.itzg.helpers.json.ObjectMappers;
1821
import me.itzg.helpers.modrinth.ModrinthCommand.DownloadDependencies;
22+
import me.itzg.helpers.modrinth.model.Project;
23+
import me.itzg.helpers.modrinth.model.ProjectType;
1924
import org.assertj.core.api.AbstractPathAssert;
2025
import org.jetbrains.annotations.NotNull;
2126
import org.junit.jupiter.api.Test;
@@ -108,12 +113,7 @@ void downloadsOnlyRequestedDependencyTypes(ModrinthCommand.DownloadDependencies
108113
stubVersionRequest(requiredDepProjectId, requiredVersionId, deps -> {});
109114
stubVersionRequest(optionalDepProjectId, optionalVersionId, deps -> {});
110115

111-
stubFor(get(urlPathMatching("/cdn/(.+)"))
112-
.willReturn(aResponse()
113-
.withBody("{{request.pathSegments.[1]}}")
114-
.withTransformers("response-template")
115-
)
116-
);
116+
stubDownload();
117117

118118
final int exitCode = new CommandLine(
119119
new ModrinthCommand()
@@ -150,6 +150,47 @@ else if (downloadDependencies == DownloadDependencies.OPTIONAL) {
150150
}
151151
}
152152

153+
@Test
154+
void failsWhenNoDependenciesForModLoader(@TempDir Path tempDir) throws JsonProcessingException {
155+
final String projectId = randomAlphanumeric(6);
156+
final String projectSlug = randomAlphabetic(5);
157+
final String versionId = randomAlphanumeric(6);
158+
final String requiredDepProjectId = randomAlphanumeric(6);
159+
160+
stubProjectBulkRequest(projectId, projectSlug);
161+
162+
stubVersionRequest(projectId, versionId, deps -> {
163+
deps.addObject()
164+
.put("project_id", requiredDepProjectId)
165+
.put("dependency_type", "required");
166+
});
167+
stubVersionRequestEmptyResponse(requiredDepProjectId);
168+
stubGetProject(requiredDepProjectId, new Project().setProjectType(ProjectType.resourcepack));
169+
170+
stubDownload();
171+
172+
final LatchingExecutionExceptionHandler executionExceptionHandler = new LatchingExecutionExceptionHandler();
173+
174+
final int exitCode = new CommandLine(
175+
new ModrinthCommand()
176+
)
177+
.setExecutionExceptionHandler(executionExceptionHandler)
178+
.execute(
179+
"--api-base-url", wm.getRuntimeInfo().getHttpBaseUrl(),
180+
"--output-directory", tempDir.toString(),
181+
"--game-version", "1.21.1",
182+
"--loader", "paper",
183+
"--projects", projectId,
184+
"--download-dependencies", DownloadDependencies.REQUIRED.name()
185+
);
186+
187+
assertThat(exitCode).isNotEqualTo(ExitCode.OK);
188+
189+
assertThat(executionExceptionHandler.getExecutionException())
190+
.isInstanceOf(InvalidParameterException.class)
191+
.hasCauseInstanceOf(NoFilesAvailableException.class);
192+
}
193+
153194
@Test
154195
void errorWhenNoApplicableVersion(@TempDir Path tempDir) {
155196
stubFor(
@@ -325,6 +366,15 @@ private void stubProjectBulkRequest(String projectId, String projectSlug) {
325366
);
326367
}
327368

369+
private void stubGetProject(String projectIdOrSlug, Project project) throws JsonProcessingException {
370+
stubFor(get(urlPathEqualTo("/v2/project/"+projectIdOrSlug))
371+
.willReturn(aResponse()
372+
.withHeader("Content-Type", "application/json")
373+
.withBody(objectMapper.writeValueAsBytes(project))
374+
)
375+
);
376+
}
377+
328378
private void stubVersionRequest(String projectId, String versionId, Consumer<ArrayNode> depsAdder) {
329379
final ArrayNode versionResp = objectMapper.createArrayNode();
330380
final ObjectNode versionNode = versionResp
@@ -347,7 +397,28 @@ private void stubVersionRequest(String projectId, String versionId, Consumer<Arr
347397
.withJsonBody(versionResp)
348398
)
349399
);
400+
}
401+
402+
private void stubVersionRequestEmptyResponse(String projectId) {
403+
final ArrayNode versionResp = objectMapper.createArrayNode();
350404

405+
stubFor(get(urlPathEqualTo("/v2/project/" + projectId + "/version"))
406+
.withQueryParam("loaders", equalTo("[\"paper\",\"spigot\"]"))
407+
.withQueryParam("game_versions", equalTo("[\"1.21.1\"]"))
408+
.willReturn(aResponse()
409+
.withHeader("Content-Type", "application/json")
410+
.withJsonBody(versionResp)
411+
)
412+
);
413+
}
414+
415+
private static void stubDownload() {
416+
stubFor(get(urlPathMatching("/cdn/(.+)"))
417+
.willReturn(aResponse()
418+
.withBody("{{request.pathSegments.[1]}}")
419+
.withTransformers("response-template")
420+
)
421+
);
351422
}
352423

353424
private static void setupStubs() {

0 commit comments

Comments
 (0)