From 58dc9954ad8c7a9c168cc915661529f88a48cf90 Mon Sep 17 00:00:00 2001 From: Geoff Bourne Date: Sun, 6 Aug 2023 12:56:01 -0500 Subject: [PATCH] cf: added support for individual mod/plugin files (#277) --- DEVELOPMENT.md | 25 -- dev/curseforge.http | 22 +- dev/wiremock/data/mappings/.gitignore | 1 + .../java/me/itzg/helpers/McImageHelper.java | 10 +- .../itzg/helpers/curseforge/CategoryInfo.java | 27 +- .../curseforge/CurseForgeApiClient.java | 92 ++++--- .../curseforge/CurseForgeFilesCommand.java | 245 ++++++++++++++++++ .../curseforge/CurseForgeFilesManifest.java | 27 ++ .../curseforge/CurseForgeInstaller.java | 57 ++-- .../curseforge/InstallCurseForgeCommand.java | 7 +- .../itzg/helpers/curseforge/ModFileIds.java | 13 + .../curseforge/ModFileRefResolver.java | 155 +++++++++++ .../helpers/curseforge/ResolvedModFile.java | 12 + .../java/me/itzg/helpers/files/Manifests.java | 11 + .../CurseForgeFilesCommandTest.java | 110 ++++++++ .../curseforge/ModFileRefResolverTest.java | 71 +++++ .../resources/curseforge/mappings/README.md | 24 ++ .../resources/curseforge/mappings/cdn.json | 13 + ...-452fda0d-5d8a-48ad-a86c-b3d73cb7738d.json | 22 ++ ...-8de5c26d-b14a-4689-815b-448fc69ecc35.json | 22 ++ ...-302af753-b376-4818-86ce-aa930437b4d1.json | 22 ++ ...-dee8eca2-74b7-4997-acca-5d4ecec61f0c.json | 22 ++ ...-b7ba62e2-168c-4aa0-aaab-92acfed0d285.json | 23 ++ ...-37c56a78-938f-44ad-ae0c-b155be83c210.json | 22 ++ ...-2dd68b95-c81b-49ad-829a-5d89c1c1b2c2.json | 22 ++ ...-781cf780-7195-4075-8193-2c997834472c.json | 22 ++ ...-9857bbc1-5406-4357-9d79-1030a97f7a72.json | 21 ++ ...-f693f170-735e-4001-8242-b4c24a1e4e21.json | 21 ++ src/test/resources/logback-test.xml | 2 + 29 files changed, 1046 insertions(+), 97 deletions(-) create mode 100644 src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java create mode 100644 src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesManifest.java create mode 100644 src/main/java/me/itzg/helpers/curseforge/ModFileIds.java create mode 100644 src/main/java/me/itzg/helpers/curseforge/ModFileRefResolver.java create mode 100644 src/main/java/me/itzg/helpers/curseforge/ResolvedModFile.java create mode 100644 src/test/java/me/itzg/helpers/curseforge/CurseForgeFilesCommandTest.java create mode 100644 src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java create mode 100644 src/test/resources/curseforge/mappings/README.md create mode 100644 src/test/resources/curseforge/mappings/cdn.json create mode 100644 src/test/resources/curseforge/mappings/v1_categories-452fda0d-5d8a-48ad-a86c-b3d73cb7738d.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_238222_files_4434385-8de5c26d-b14a-4689-815b-448fc69ecc35.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_238222_files_4593548-302af753-b376-4818-86ce-aa930437b4d1.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_238222_files_4615177-dee8eca2-74b7-4997-acca-5d4ecec61f0c.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_238222_files_4644453-b7ba62e2-168c-4aa0-aaab-92acfed0d285.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_31054_files_3677516-37c56a78-938f-44ad-ae0c-b155be83c210.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_search-2dd68b95-c81b-49ad-829a-5d89c1c1b2c2.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_search-781cf780-7195-4075-8193-2c997834472c.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_search-9857bbc1-5406-4357-9d79-1030a97f7a72.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_search-f693f170-735e-4001-8242-b4c24a1e4e21.json diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 5b137498..d57df471 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -6,28 +6,3 @@ Beyond the unit tests, ad hoc "integration testing" can be done by running via G ./gradlew run --args="assert fileExists build.gradle" ``` -## Creating a new release - -The Github Actions workflow `publish-release` will take of performing the release build, but the tag needs to be created with the release tasks shown below. - -### Patch release - -```shell -./gradlew release -PpushReleaseTag=true -``` - -### Minor release - -```shell -./gradlew releaseMinorVersion -PpushReleaseTag=true -``` - -## Creating a pre-release - -When needing to test a pre-release, such as a PR's changes, a pre-release can be run locally. - -Ensure `$HOME/.jreleaser/config.properties` contains `JRELEASER_GITHUB_TOKEN` set with a token with repo access. - -Invoke the gradle task `jreleaseRelease`. - -The release and artifacts are located at \ No newline at end of file diff --git a/dev/curseforge.http b/dev/curseforge.http index ff24ff7e..12add6e4 100644 --- a/dev/curseforge.http +++ b/dev/curseforge.http @@ -6,7 +6,18 @@ x-api-key: {{cfApiKey}} <> 2023-03-30T083426.200.json ### -GET https://api.curseforge.com/v1/mods/350464 +# mod +GET https://api.curseforge.com/v1/mods/search?gameId=432&slug=jei&classId=6 +x-api-key: {{cfApiKey}} + +### +# mod +GET https://api.curseforge.com/v1/mods/238222 +x-api-key: {{cfApiKey}} + +### +# bukkit-plugin +GET https://api.curseforge.com/v1/mods/31043 x-api-key: {{cfApiKey}} ### @@ -14,7 +25,7 @@ GET https://api.curseforge.com/v1/mods/694605/files/4098018/download-url x-api-key: {{cfApiKey}} ### -GET https://api.curseforge.com/v1/mods/369096/files/4560441 +GET https://api.curseforge.com/v1/mods/238222/files/4644453 x-api-key: {{cfApiKey}} <> 2023-04-01T192357.200.json @@ -31,4 +42,9 @@ x-api-key: {{cfApiKey}} GET https://api.curseforge.com/v1/mods/707734/files/4415193 x-api-key: {{cfApiKey}} -<> 2023-03-30T080528.403.html \ No newline at end of file +<> 2023-03-30T080528.403.html + + +### +GET http://localhost:8080/v1/mods/238222/files/4615177 +x-api-key: {{cfApiKey}} diff --git a/dev/wiremock/data/mappings/.gitignore b/dev/wiremock/data/mappings/.gitignore index e69de29b..94a2dd14 100644 --- a/dev/wiremock/data/mappings/.gitignore +++ b/dev/wiremock/data/mappings/.gitignore @@ -0,0 +1 @@ +*.json \ No newline at end of file diff --git a/src/main/java/me/itzg/helpers/McImageHelper.java b/src/main/java/me/itzg/helpers/McImageHelper.java index 88de502a..ef78e98b 100644 --- a/src/main/java/me/itzg/helpers/McImageHelper.java +++ b/src/main/java/me/itzg/helpers/McImageHelper.java @@ -12,6 +12,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import me.itzg.helpers.assertcmd.AssertCommand; +import me.itzg.helpers.curseforge.CurseForgeFilesCommand; import me.itzg.helpers.curseforge.InstallCurseForgeCommand; import me.itzg.helpers.errors.ExceptionHandler; import me.itzg.helpers.errors.ExitCodeMapper; @@ -52,6 +53,7 @@ Asciify.class, AssertCommand.class, CompareVersionsCommand.class, + CurseForgeFilesCommand.class, FindCommand.class, GetCommand.class, HashCommand.class, @@ -87,8 +89,7 @@ public class McImageHelper { //language=RegExp public static final String VERSION_REGEX = "\\d+(\\.\\d+)+"; - @SuppressWarnings("unused") - @CommandLine.Option(names = {"-h", + @Option(names = {"-h", "--help"}, usageHelp = true, description = "Show this usage and exit") boolean showHelp; @@ -130,6 +131,7 @@ private static void setLevel(boolean enabled, Level level) { @Getter boolean silent; + @Getter private static String version; public static void main(String[] args) { @@ -165,10 +167,6 @@ private static String loadVersion() throws IOException { return "???"; } - public static String getVersion() { - return version; - } - public static class AppVersionProvider implements IVersionProvider { @Override public String[] getVersion() { diff --git a/src/main/java/me/itzg/helpers/curseforge/CategoryInfo.java b/src/main/java/me/itzg/helpers/curseforge/CategoryInfo.java index fcb2de85..bce785cb 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CategoryInfo.java +++ b/src/main/java/me/itzg/helpers/curseforge/CategoryInfo.java @@ -1,12 +1,31 @@ package me.itzg.helpers.curseforge; +import java.util.Map; import lombok.AllArgsConstructor; import me.itzg.helpers.curseforge.model.Category; -import java.util.Map; - @AllArgsConstructor -class CategoryInfo { +public class CategoryInfo { Map contentClassIds; - int modpackClassId; + Map slugIds; + + public int getClassIdForSlug(String categorySlug) { + final Integer classId = slugIds.get(categorySlug); + if (classId != null) { + return classId; + } + else { + throw new IllegalArgumentException("Unexpected category: " + categorySlug); + } + } + + public Category getCategory(int categoryId) { + final Category category = contentClassIds.get(categoryId); + if (category != null) { + return category; + } + else { + throw new IllegalArgumentException("Unknown category ID: " + categoryId); + } + } } diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java index 8b700796..30cbd98c 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java @@ -6,9 +6,10 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import lombok.extern.slf4j.Slf4j; import me.itzg.helpers.curseforge.model.Category; import me.itzg.helpers.curseforge.model.CurseForgeFile; @@ -27,17 +28,27 @@ import me.itzg.helpers.http.SharedFetch; import me.itzg.helpers.http.UriBuilder; import me.itzg.helpers.json.ObjectMappers; +import org.slf4j.Logger; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @Slf4j public class CurseForgeApiClient implements AutoCloseable { + + public static final String CATEGORY_MODPACKS = "modpacks"; + public static final String CATEGORY_MC_MODS = "mc-mods"; + public static final String CATEGORY_BUKKIT_PLUGINS = "bukkit-plugins"; + public static final String CATEGORY_WORLDS = "worlds"; + private static final String API_KEY_HEADER = "x-api-key"; + static final String MINECRAFT_GAME_ID = "432"; private final SharedFetch preparedFetch; private final UriBuilder uriBuilder; private final String gameId; + private final ConcurrentHashMap cachedMods = new ConcurrentHashMap<>(); + public CurseForgeApiClient(String apiBaseUrl, String apiKey, SharedFetch.Options sharedFetchOptions, String gameId ) { this.preparedFetch = Fetch.sharedFetch("install-curseforge", @@ -48,57 +59,66 @@ public CurseForgeApiClient(String apiBaseUrl, String apiKey, SharedFetch.Options this.gameId = gameId; } + static FileDownloadStatusHandler modFileDownloadStatusHandler(Path outputDir, Logger log) { + return (status, uri, f) -> { + switch (status) { + case SKIP_FILE_EXISTS: + log.info("Mod file {} already exists", outputDir.relativize(f)); + break; + case DOWNLOADED: + log.info("Downloaded mod file {}", outputDir.relativize(f)); + break; + } + }; + } + @Override public void close() { preparedFetch.close(); } - CategoryInfo loadModpacksCategoryInfo(Set applicableClassIdSlugs) { + Mono loadCategoryInfo(Collection applicableClassIdSlugs) { return preparedFetch // get only categories that are classes, like mc-mods - .fetch(uriBuilder.resolve("/categories?gameId={gameId}&classesOnly=true", gameId)) + .fetch(uriBuilder.resolve("/v1/categories?gameId={gameId}&classesOnly=true", gameId)) .toObject(GetCategoriesResponse.class) .assemble() .flatMap(resp -> { final Map contentClassIds = new HashMap<>(); - Integer modpackClassId = null; + final Map slugIds = new HashMap<>(); for (final Category category : resp.getData()) { if (applicableClassIdSlugs.contains(category.getSlug())) { contentClassIds.put(category.getId(), category); - } - if (category.getSlug().equals(CurseForgeInstaller.CATEGORY_SLUG_MODPACKS)) { - modpackClassId = category.getId(); + slugIds.put(category.getSlug(), category.getId()); } } - if (modpackClassId == null) { - return Mono.error(new GenericException("Unable to lookup classId for modpacks")); - } - - return Mono.just(new CategoryInfo(contentClassIds, modpackClassId)); + return Mono.just(new CategoryInfo(contentClassIds, slugIds)); } - ) - .block(); + ); } - CurseForgeMod searchMod(String slug, CategoryInfo categoryInfo) { - final ModsSearchResponse searchResponse = preparedFetch.fetch( - uriBuilder.resolve("/mods/search?gameId={gameId}&slug={slug}&classId={classId}", - gameId, slug, categoryInfo.modpackClassId + Mono searchMod(String slug, int classId) { + return preparedFetch.fetch( + uriBuilder.resolve("/v1/mods/search?gameId={gameId}&slug={slug}&classId={classId}", + gameId, slug, classId ) ) .toObject(ModsSearchResponse.class) - .execute(); - - if (searchResponse.getData() == null || searchResponse.getData().isEmpty()) { - throw new GenericException("No mods found with slug={}" + slug); - } else if (searchResponse.getData().size() > 1) { - throw new GenericException("More than one mod found with slug=" + slug); - } else { - return searchResponse.getData().get(0); - } - + .assemble() + .flatMap(searchResponse -> { + if (searchResponse.getData() == null || searchResponse.getData().isEmpty()) { + return Mono.error(new GenericException("No mods found with slug=" + slug)); + } + else if (searchResponse.getData().size() > 1) { + return Mono.error(new GenericException("More than one mod found with slug=" + slug)); + } + else { + return Mono.just(searchResponse.getData().get(0)); + } + }) + .doOnNext(curseForgeMod -> cachedMods.put(curseForgeMod.getId(), curseForgeMod)); } /** @@ -110,7 +130,7 @@ public CurseForgeFile resolveModpackFile( ) { // NOTE latestFiles in mod is only one or two files, so retrieve the full list instead final GetModFilesResponse resp = preparedFetch.fetch( - uriBuilder.resolve("/mods/{modId}/files", mod.getId() + uriBuilder.resolve("/v1/mods/{modId}/files", mod.getId() ) ) .toObject(GetModFilesResponse.class) @@ -135,7 +155,7 @@ Mono slugToId(CategoryInfo categoryInfo, ) { return preparedFetch .fetch( - uriBuilder.resolve("/mods/search?gameId={gameId}&slug={slug}", gameId, slug) + uriBuilder.resolve("/v1/mods/search?gameId={gameId}&slug={slug}", gameId, slug) ) .toObject(ModsSearchResponse.class) .assemble() @@ -153,13 +173,19 @@ public Mono getModInfo( ) { log.debug("Getting mod metadata for {}", projectID); + final CurseForgeMod cached = cachedMods.get(projectID); + if (cached != null) { + return Mono.just(cached); + } + return preparedFetch.fetch( - uriBuilder.resolve("/mods/{modId}", projectID) + uriBuilder.resolve("/v1/mods/{modId}", projectID) ) .toObject(GetModResponse.class) .assemble() .checkpoint("Getting mod info for " + projectID) - .map(GetModResponse::getData); + .map(GetModResponse::getData) + .doOnNext(curseForgeMod -> cachedMods.put(curseForgeMod.getId(), curseForgeMod)); } public Mono getModFileInfo( @@ -168,7 +194,7 @@ public Mono getModFileInfo( log.debug("Getting mod file metadata for {}:{}", projectID, fileID); return preparedFetch.fetch( - uriBuilder.resolve("/mods/{modId}/files/{fileId}", projectID, fileID) + uriBuilder.resolve("/v1/mods/{modId}/files/{fileId}", projectID, fileID) ) .toObject(GetModFileResponse.class) .assemble() diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java new file mode 100644 index 00000000..adb1f233 --- /dev/null +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java @@ -0,0 +1,245 @@ +package me.itzg.helpers.curseforge; + +import static me.itzg.helpers.curseforge.CurseForgeApiClient.*; +import static me.itzg.helpers.curseforge.ModFileRefResolver.idsFrom; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import me.itzg.helpers.curseforge.CurseForgeFilesManifest.FileEntry; +import me.itzg.helpers.curseforge.model.Category; +import me.itzg.helpers.curseforge.model.CurseForgeFile; +import me.itzg.helpers.curseforge.model.CurseForgeMod; +import me.itzg.helpers.curseforge.model.FileDependency; +import me.itzg.helpers.curseforge.model.ModLoaderType; +import me.itzg.helpers.errors.GenericException; +import me.itzg.helpers.errors.InvalidParameterException; +import me.itzg.helpers.files.Manifests; +import me.itzg.helpers.http.SharedFetchArgs; +import org.jetbrains.annotations.NotNull; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.ExitCode; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +@Command(name = "curseforge-files", description = "Download and manage individual mod/plugin files from CurseForge") +@Slf4j +public class CurseForgeFilesCommand implements Callable { + + public static final List ALLOWED_CATEGORIES = Arrays.asList( + CATEGORY_MC_MODS, + CATEGORY_BUKKIT_PLUGINS + ); + + private static final Map categorySubdirs = new HashMap<>(); + + static { + categorySubdirs.put(CATEGORY_MC_MODS, "mods"); + categorySubdirs.put(CATEGORY_BUKKIT_PLUGINS, "plugins"); + } + + @Option(names = {"--help", "-h"}, usageHelp = true) + boolean help; + + @Option(names = {"--output-directory", "-o"}, defaultValue = ".", paramLabel = "DIR") + Path outputDir; + + @Option(names = "--default-category", description = "When providing slugs, a category is required to qualify those") + public void setSlugCategory(String defaultCategory) { + if (!ALLOWED_CATEGORIES.contains(defaultCategory)) { + throw new InvalidParameterException("Category can only be one of " + String.join(", ", ALLOWED_CATEGORIES)); + } + this.defaultCategory = defaultCategory; + } + + String defaultCategory; + + @Option(names = "--api-base-url", defaultValue = "${env:CF_API_BASE_URL:-https://api.curseforge.com}", + description = "Allows for overriding the CurseForge Eternal API used" + + "%nCan also be passed via CF_API_BASE_URL") + String apiBaseUrl; + + @Option(names = "--api-key", defaultValue = "${env:" + CurseForgeInstaller.API_KEY_VAR + "}", + required = true, + description = "An API key allocated from the Eternal developer console at " + + CurseForgeInstaller.ETERNAL_DEVELOPER_CONSOLE_URL + + "%nCan also be passed via " + CurseForgeInstaller.API_KEY_VAR + ) + String apiKey; + + @Option(names = "--game-version", defaultValue = "${env:VERSION}", + description = "The Minecraft version" + + "%nCan also be passed via VERSION" + ) + String gameVersion; + + @Option(names = "--mod-loader", + description = "One of ${COMPLETION-CANDIDATES}" + ) + ModLoaderType modLoaderType; + + @ArgGroup(exclusive = false) + SharedFetchArgs sharedFetchArgs = new SharedFetchArgs(); + + @Parameters(split = ",", paramLabel = "REF", + description = "Can be |':'," + + " |'@'," + + " |," + + " project page URL, file page URL," + + " '@'" + + "%nIf not specified, any previous mod/plugin files are removed") + List modFileRefs; + + @Override + public Integer call() throws Exception { + try (CurseForgeApiClient apiClient = new CurseForgeApiClient( + apiBaseUrl, apiKey, sharedFetchArgs.options(), + CurseForgeApiClient.MINECRAFT_GAME_ID + )) { + + final CurseForgeFilesManifest oldManifest = Manifests.load(outputDir, CurseForgeFilesManifest.ID, + CurseForgeFilesManifest.class + ); + + final Map previousFiles = buildPreviousFilesFromManifest(oldManifest); + + final CurseForgeFilesManifest newManifest = + apiClient.loadCategoryInfo(Arrays.asList(CATEGORY_MC_MODS, CATEGORY_BUKKIT_PLUGINS)) + .flatMap(categoryInfo -> + processModFileRefs(categoryInfo, previousFiles, apiClient) + .map(entries -> CurseForgeFilesManifest.builder() + .entries(entries) + .build())) + .block(); + + if (oldManifest != null && newManifest != null) { + Manifests.cleanup(outputDir, + mapFilePathsFromEntries(oldManifest), + mapFilePathsFromEntries(newManifest), + s -> log.info("Removing old file {}", s) + ); + } + if (newManifest != null && newManifest.getEntries() != null + && !newManifest.getEntries().isEmpty() + ) { + Manifests.save(outputDir, CurseForgeFilesManifest.ID, newManifest); + } + else { + Manifests.remove(outputDir, CurseForgeFilesManifest.ID); + } + } + + return ExitCode.OK; + } + + @NotNull + private Mono> processModFileRefs(CategoryInfo categoryInfo, + Map previousFiles, CurseForgeApiClient apiClient + ) { + if (modFileRefs == null || modFileRefs.isEmpty()) { + return Mono.just(Collections.emptyList()); + } + + final ModFileRefResolver modFileRefResolver = new ModFileRefResolver(apiClient, categoryInfo); + + return modFileRefResolver.resolveModFiles(modFileRefs, defaultCategory, gameVersion, modLoaderType) + .flatMapMany(modFiles -> { + final Set requestedModIds = modFiles.stream() + .map(CurseForgeFile::getModId) + .collect(Collectors.toSet()); + + return Flux.fromIterable(modFiles) + .flatMap(modFile -> { + for (final FileDependency dependency : modFile.getDependencies()) { + if (!requestedModIds.contains(dependency.getModId())) { + log.warn("The mod file {} depends on mod project ID {}, but that is not listed", + modFile.getDisplayName(), dependency.getModId() + ); + } + } + final ModFileIds modFileIds = idsFrom(modFile); + final FileEntry entry = previousFiles.get(modFileIds); + if (entry != null + && Files.exists(outputDir.resolve(entry.getFilePath())) + ) { + log.debug("Mod file {} already exists at {}", modFile.getFileName(), entry.getFilePath()); + return Mono.just(entry); + } + else { + return retrieveModFile(apiClient, categoryInfo, modFile) + .map(path -> new FileEntry(modFileIds, outputDir.relativize(path).toString())); + } + }); + } + ) + .collectList(); + } + + @NotNull + private static Map buildPreviousFilesFromManifest(CurseForgeFilesManifest oldManifest) { + return oldManifest != null ? + oldManifest.getEntries().stream() + .collect(Collectors.toMap( + FileEntry::getIds, + fileEntry -> fileEntry + )) + : Collections.emptyMap(); + } + + @NotNull + private Mono retrieveModFile(CurseForgeApiClient apiClient, CategoryInfo categoryInfo, + CurseForgeFile curseForgeFile + ) { + return apiClient.getModInfo(curseForgeFile.getModId()) + .flatMap(curseForgeMod -> + setupSubdir(categoryInfo, curseForgeMod) + .flatMap(subdir -> + apiClient.download(curseForgeFile, + outputDir.resolve(subdir).resolve(curseForgeFile.getFileName()), + modFileDownloadStatusHandler(outputDir, log) + ) + )); + } + + @NotNull + private Mono setupSubdir(CategoryInfo categoryInfo, CurseForgeMod curseForgeMod) { + return Mono.defer(() -> { + final Category category = categoryInfo.getCategory(curseForgeMod.getClassId()); + + final String subdir = categorySubdirs.get(category.getSlug()); + if (subdir == null) { + return Mono.error(new InvalidParameterException( + String.format("Category %s does not have a known subdir", category.getName()))); + } + + try { + //noinspection BlockingMethodInNonBlockingContext due to subscribeOn + Files.createDirectories(outputDir.resolve(subdir)); + } catch (IOException e) { + return Mono.error(new GenericException("Failed to create mod file directory", e)); + } + + return Mono.just(subdir); + }) + .subscribeOn(Schedulers.boundedElastic()); + } + + @NotNull + private static List mapFilePathsFromEntries(CurseForgeFilesManifest oldManifest) { + return oldManifest.entries.stream().map(FileEntry::getFilePath).collect(Collectors.toList()); + } + +} diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesManifest.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesManifest.java new file mode 100644 index 00000000..493cdae9 --- /dev/null +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesManifest.java @@ -0,0 +1,27 @@ +package me.itzg.helpers.curseforge; + +import java.util.List; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import me.itzg.helpers.files.BaseManifest; + +@Getter +@SuperBuilder +@Jacksonized +public class CurseForgeFilesManifest extends BaseManifest { + + public static final String ID = "curseforge-files"; + + @Data + @Builder + @Jacksonized + public static class FileEntry { + final ModFileIds ids; + final String filePath; + } + + List entries; +} diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java index e21941b9..97275203 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java @@ -3,9 +3,12 @@ import static java.util.Collections.emptySet; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; +import static me.itzg.helpers.curseforge.CurseForgeApiClient.modFileDownloadStatusHandler; import static me.itzg.helpers.singles.MoreCollections.safeStreamFrom; +import java.io.File; import java.io.IOException; +import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -43,7 +46,9 @@ import me.itzg.helpers.files.ResultsFileWriter; import me.itzg.helpers.forge.ForgeInstaller; import me.itzg.helpers.http.FailedRequestException; +import me.itzg.helpers.http.Fetch; import me.itzg.helpers.http.SharedFetch; +import me.itzg.helpers.http.Uris; import me.itzg.helpers.json.ObjectMappers; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -57,11 +62,9 @@ public class CurseForgeInstaller { public static final String MODPACK_ZIP_VAR = "CF_MODPACK_ZIP"; public static final String ETERNAL_DEVELOPER_CONSOLE_URL = "https://console.curseforge.com/"; - private static final String MINECRAFT_GAME_ID = "432"; public static final String CURSEFORGE_ID = "curseforge"; public static final String LEVEL_DAT_SUFFIX = "/level.dat"; public static final int LEVEL_DAT_SUFFIX_LEN = LEVEL_DAT_SUFFIX.length(); - public static final String CATEGORY_SLUG_MODPACKS = "modpacks"; public static final String REPO_SUBDIR_MODPACKS = "modpacks"; public static final String REPO_SUBDIR_MODS = "mods"; public static final String REPO_SUBDIR_WORLDS = "worlds"; @@ -70,7 +73,7 @@ public class CurseForgeInstaller { private final Path resultsFile; @Getter @Setter - private String apiBaseUrl = "https://api.curseforge.com/v1"; + private String apiBaseUrl = "https://api.curseforge.com"; @Getter @Setter private String apiKey; @@ -98,9 +101,10 @@ public class CurseForgeInstaller { private Path downloadsRepo; private final Set applicableClassIdSlugs = new HashSet<>(Arrays.asList( - "mc-mods", - "bukkit-plugins", - "worlds" + CurseForgeApiClient.CATEGORY_MODPACKS, + CurseForgeApiClient.CATEGORY_MC_MODS, + CurseForgeApiClient.CATEGORY_BUKKIT_PLUGINS, + CurseForgeApiClient.CATEGORY_WORLDS )); /** @@ -121,12 +125,24 @@ public void installFromModpackZip(Path modpackZip, String slug) throws IOExcepti /** * @throws MissingModsException if any mods need to be manually downloaded */ - public void installFromModpackManifest(Path modpackManifestPath, String slug) throws IOException { - requireNonNull(modpackManifestPath, "modpackManifest is required"); + public void installFromModpackManifest(String modpackManifestLoc, String slug) throws IOException { + requireNonNull(modpackManifestLoc, "modpackManifest is required"); install(slug, context -> { - final MinecraftModpackManifest modpackManifest = ObjectMappers.defaultMapper() - .readValue(modpackManifestPath.toFile(), MinecraftModpackManifest.class); + final MinecraftModpackManifest modpackManifest; + if (Uris.isUri(modpackManifestLoc)) { + modpackManifest = Fetch.fetch(URI.create(modpackManifestLoc)) + .toObject(MinecraftModpackManifest.class) + .assemble() + .block(); + if (modpackManifest == null) { + throw new GenericException("Modpack manifest could not be fetched"); + } + } + else { + modpackManifest = ObjectMappers.defaultMapper() + .readValue(new File(modpackManifestLoc), MinecraftModpackManifest.class); + } processModpackManifest(context, modpackManifest, () -> new OverridesResult(Collections.emptyList(), null) @@ -170,10 +186,11 @@ void install(String slug, InstallationEntryPoint entryPoint) throws IOException try ( CurseForgeApiClient cfApi = new CurseForgeApiClient( apiBaseUrl, apiKey, sharedFetchOptions, - MINECRAFT_GAME_ID + CurseForgeApiClient.MINECRAFT_GAME_ID ) ) { - final CategoryInfo categoryInfo = cfApi.loadModpacksCategoryInfo(applicableClassIdSlugs); + final CategoryInfo categoryInfo = cfApi.loadCategoryInfo(applicableClassIdSlugs) + .block(); entryPoint.install( new InstallContext(slug, cfApi, categoryInfo, manifest) @@ -204,7 +221,10 @@ interface InstallationEntryPoint { } private void installByRetrievingModpackZip(InstallContext context, String fileMatcher, Integer fileId) throws IOException { - final CurseForgeMod curseForgeMod = context.cfApi.searchMod(context.slug, context.categoryInfo); + final CurseForgeMod curseForgeMod = context.cfApi.searchMod(context.slug, + context.categoryInfo.getClassIdForSlug(CurseForgeApiClient.CATEGORY_MODPACKS) + ) + .block(); resolveModpackFileAndProcess(context, curseForgeMod, fileId, fileMatcher); } @@ -826,16 +846,7 @@ else if (cfFile.getDownloadUrl() == null) { } } else { - return context.cfApi.download(cfFile, outputFile, (status, uri, f) -> { - switch (status) { - case SKIP_FILE_EXISTS: - log.info("Mod file {} already exists", outputDir.relativize(f)); - break; - case DOWNLOADED: - log.info("Downloaded mod file {}", outputDir.relativize(f)); - break; - } - }) + return context.cfApi.download(cfFile, outputFile, modFileDownloadStatusHandler(outputDir, log)) .map(ResolveResult::new); } } diff --git a/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java b/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java index 59aea6db..538b89c6 100644 --- a/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java +++ b/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java @@ -32,7 +32,7 @@ public class InstallCurseForgeCommand implements Callable { @Option(names = {"--help","-h"}, usageHelp = true) boolean help; - @Option(names = "--output-directory", defaultValue = ".", paramLabel = "DIR") + @Option(names = {"--output-directory", "-o"}, defaultValue = ".", paramLabel = "DIR") Path outputDirectory; @Option(names = "--results-file", description = ResultsFileWriter.OPTION_DESCRIPTION, paramLabel = "FILE") @@ -59,9 +59,10 @@ public class InstallCurseForgeCommand implements Callable { Path modpackZip; @Option(names = "--modpack-manifest", paramLabel = "PATH", - description = "Similar to --modpack-zip but provide the manifest.json from the modpack" + description = "Similar to --modpack-zip but provide the manifest.json from the modpack." + + "%nCan be a local file path or a URL to a manifest." ) - Path modpackManifest; + String modpackManifest; @Option(names = "--downloads-repo", paramLabel = "DIR", description = "A local directory that will supply pre-downloaded mod and modpack files that " + diff --git a/src/main/java/me/itzg/helpers/curseforge/ModFileIds.java b/src/main/java/me/itzg/helpers/curseforge/ModFileIds.java new file mode 100644 index 00000000..33e0d634 --- /dev/null +++ b/src/main/java/me/itzg/helpers/curseforge/ModFileIds.java @@ -0,0 +1,13 @@ +package me.itzg.helpers.curseforge; + +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class ModFileIds { + final int modId; + final int fileId; +} diff --git a/src/main/java/me/itzg/helpers/curseforge/ModFileRefResolver.java b/src/main/java/me/itzg/helpers/curseforge/ModFileRefResolver.java new file mode 100644 index 00000000..e0dd3ea9 --- /dev/null +++ b/src/main/java/me/itzg/helpers/curseforge/ModFileRefResolver.java @@ -0,0 +1,155 @@ +package me.itzg.helpers.curseforge; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import me.itzg.helpers.curseforge.model.CurseForgeFile; +import me.itzg.helpers.curseforge.model.CurseForgeMod; +import me.itzg.helpers.curseforge.model.ModLoaderType; +import me.itzg.helpers.errors.GenericException; +import me.itzg.helpers.errors.InvalidParameterException; +import org.jetbrains.annotations.NotNull; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +public class ModFileRefResolver { + + private static final Pattern REF_FORMATS = Pattern.compile( + "https://(www|legacy).curseforge.com/minecraft/(?[A-Za-z-]+)/(?[A-Za-z-]+)(/files/(?\\d+))?" + + "|" + + "((?\\d+)|(?[A-Za-z-]+))((:(?\\d+))|(@(?.+)))?" + ); + private final CurseForgeApiClient apiClient; + private final CategoryInfo categoryInfo; + + public ModFileRefResolver(CurseForgeApiClient apiClient, CategoryInfo categoryInfo) { + this.apiClient = apiClient; + this.categoryInfo = categoryInfo; + } + + public Mono> resolveModFiles(List modFileRefs, String defaultCategory, + String gameVersion, + ModLoaderType modLoaderType + ) { + return expandFileListings(modFileRefs) + .flatMap(ref -> { + final Matcher m = REF_FORMATS.matcher(ref); + if (!m.matches()) { + return Mono.error(new InvalidParameterException("Mod file reference is not valid: " + ref)); + } + + final Integer modId = Optional.ofNullable(m.group("modId")).map(Integer::parseInt).orElse(null); + final String fileIdWithModId = m.group("fileId"); + if (modId != null && fileIdWithModId != null) { + return apiClient.getModFileInfo(modId, Integer.parseInt(fileIdWithModId)); + } + + final String category = Optional.ofNullable(m.group("category")) + .orElse(defaultCategory); + + return resolveMod(modId, m, category) + .flatMap(curseForgeMod -> + resolveModFileFromMod(ref, gameVersion, category, modLoaderType, curseForgeMod, m) + ); + }) + .collectList(); + + } + + @NotNull + private static Flux expandFileListings(List modFileRefs) { + return Flux.fromStream(modFileRefs.stream() + // handle @-file containing refs + .flatMap(ref -> { + if (ref.startsWith("@")) { + try { + return Files.readAllLines(Paths.get(ref.substring(1))).stream() + .map(s -> s + .replaceFirst("#.*", "") + .trim() + ) + .filter(s -> !s.isEmpty()); + } catch (IOException e) { + throw new GenericException("Reading mod file refs from file", e); + } + } + else { + return Stream.of(ref); + } + }) + ) + .subscribeOn(Schedulers.boundedElastic()); + } + + private Mono resolveModFileFromMod(String ref, String gameVersion, String category, + ModLoaderType modLoaderType, CurseForgeMod mod, Matcher m + ) { + final String fileId = Optional.ofNullable(m.group("fileIdInUrl")) + .orElseGet(() -> m.group("fileId")); + + if (fileId != null) { + return apiClient.getModFileInfo(mod.getId(), Integer.parseInt(fileId)); + } + + if (gameVersion == null) { + return Mono.error( + new InvalidParameterException("Game version is required to resolve mod reference: " + ref)); + } + if (modLoaderType == null && Objects.equals(category, CurseForgeApiClient.CATEGORY_MC_MODS)) { + return Mono.error( + new InvalidParameterException("Mod loader is required to resolve mod reference: " + ref)); + } + + final String fileMatcherStr = m.group("fileMatcher"); + return mod.getLatestFilesIndexes().stream() + .filter(fileIndex -> + fileIndex.getGameVersion().equals(gameVersion) + && ( + // only mods require a specific mod loader, plugins are not that specific + !Objects.equals(category, CurseForgeApiClient.CATEGORY_MC_MODS) + || fileIndex.getModLoader().equals(modLoaderType)) + && (fileMatcherStr == null || fileIndex.getFilename().contains(fileMatcherStr)) + ) + .findFirst() + .map(fileIndex -> mod.getLatestFiles().stream() + .filter(curseForgeFile -> curseForgeFile.getId() == fileIndex.getFileId()) + .findFirst() + .map(Mono::just) + .orElseGet(() -> apiClient.getModFileInfo(mod.getId(), fileIndex.getFileId())) + ) + .orElseGet(() -> Mono.error( + new InvalidParameterException("Unable to find matching file for reference: " + ref)) + ); + } + + public static ModFileIds idsFrom(CurseForgeFile curseForgeFile) { + return new ModFileIds(curseForgeFile.getModId(), curseForgeFile.getId()); + } + + private Mono resolveMod(Integer modId, Matcher m, String categorySlug) { + final Mono resolvedMod; + if (modId != null) { + resolvedMod = apiClient.getModInfo(modId); + } + else { + final String slug = Optional.ofNullable(m.group("slugInUrl")) + .orElseGet(() -> m.group("slug")); + + if (categorySlug == null) { + throw new InvalidParameterException("Default category is required to resolve " + slug); + } + + final int categoryClassId = categoryInfo.getClassIdForSlug(categorySlug); + + resolvedMod = apiClient.searchMod(slug, categoryClassId); + } + return resolvedMod; + } +} diff --git a/src/main/java/me/itzg/helpers/curseforge/ResolvedModFile.java b/src/main/java/me/itzg/helpers/curseforge/ResolvedModFile.java new file mode 100644 index 00000000..b725008b --- /dev/null +++ b/src/main/java/me/itzg/helpers/curseforge/ResolvedModFile.java @@ -0,0 +1,12 @@ +package me.itzg.helpers.curseforge; + +import lombok.Data; + +@Data +public class ResolvedModFile { + final ModFileIds ids; + + String categoryClassSlug; + + +} diff --git a/src/main/java/me/itzg/helpers/files/Manifests.java b/src/main/java/me/itzg/helpers/files/Manifests.java index afd2e9ac..991de985 100644 --- a/src/main/java/me/itzg/helpers/files/Manifests.java +++ b/src/main/java/me/itzg/helpers/files/Manifests.java @@ -139,4 +139,15 @@ public static Path save(Path outputDir, String id, M ma private Manifests() { } + + public static void remove(Path outputDir, String id) { + final Path manifestPath = buildManifestPath(outputDir, id); + if (Files.exists(manifestPath)) { + try { + Files.delete(manifestPath); + } catch (IOException e) { + throw new ManifestException("Failed to remove manifest file", e); + } + } + } } diff --git a/src/test/java/me/itzg/helpers/curseforge/CurseForgeFilesCommandTest.java b/src/test/java/me/itzg/helpers/curseforge/CurseForgeFilesCommandTest.java new file mode 100644 index 00000000..c863a5ed --- /dev/null +++ b/src/test/java/me/itzg/helpers/curseforge/CurseForgeFilesCommandTest.java @@ -0,0 +1,110 @@ +package me.itzg.helpers.curseforge; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; +import picocli.CommandLine; + +class CurseForgeFilesCommandTest { + + @TempDir + Path tempDir; + + @RegisterExtension + static WireMockExtension wm = WireMockExtension.newInstance() + .options(wireMockConfig() + .dynamicPort() + .usingFilesUnderClasspath("curseforge") + .extensions(new ResponseTemplateTransformer(false)) + ) + .configureStaticDsl(true) + .build(); + + @Test + void oneOfEachCategoryAndUpgrade() { + int exitCode = new CommandLine( + new CurseForgeFilesCommand() + ) + .execute( + "--api-base-url", wm.baseUrl(), + "--api-key", "test", + "--output-directory", tempDir.toString(), + "--default-category", "mc-mods", + "jei:4434385", + "https://www.curseforge.com/minecraft/bukkit-plugins/worldguard/files/3677516" + ); + + assertThat(exitCode).isEqualTo(0); + + assertThat(tempDir.resolve("mods/jei-1.18.2-forge-10.2.1.1003.jar")).exists(); + assertThat(tempDir.resolve("plugins/worldguard-bukkit-7.0.7-dist.jar")).exists(); + + // upgrade jei + exitCode = new CommandLine( + new CurseForgeFilesCommand() + ) + .execute( + "--api-base-url", wm.baseUrl(), + "--api-key", "test", + "--output-directory", tempDir.toString(), + "--default-category", "mc-mods", + "jei:4593548", + "https://www.curseforge.com/minecraft/bukkit-plugins/worldguard/files/3677516" + ); + + assertThat(exitCode).isEqualTo(0); + + assertThat(tempDir.resolve("mods/jei-1.18.2-forge-10.2.1.1003.jar")).doesNotExist(); + assertThat(tempDir.resolve("mods/jei-1.18.2-forge-10.2.1.1005.jar")).exists(); + assertThat(tempDir.resolve("plugins/worldguard-bukkit-7.0.7-dist.jar")).exists(); + + // ...and remove all + exitCode = new CommandLine( + new CurseForgeFilesCommand() + ) + .execute( + "--api-base-url", wm.baseUrl(), + "--api-key", "test", + "--output-directory", tempDir.toString(), + "--default-category", "mc-mods" + ); + + assertThat(exitCode).isEqualTo(0); + + assertThat(tempDir.resolve("mods/jei-1.18.2-forge-10.2.1.1005.jar")).doesNotExist(); + assertThat(tempDir.resolve("plugins/worldguard-bukkit-7.0.7-dist.jar")).doesNotExist(); + } + + @Test + void usingListingFile() throws IOException { + final Path listingFile = Files.write(tempDir.resolve("listing.txt"), + Arrays.asList("# Comment", "jei:4434385", "", "https://www.curseforge.com/minecraft/bukkit-plugins/worldguard/files/3677516") + ); + + int exitCode = new CommandLine( + new CurseForgeFilesCommand() + ) + .execute( + "--api-base-url", wm.baseUrl(), + "--api-key", "test", + "--output-directory", tempDir.toString(), + "--default-category", "mc-mods", + "@" + listingFile + ); + + assertThat(exitCode).isEqualTo(0); + + assertThat(tempDir.resolve("mods/jei-1.18.2-forge-10.2.1.1003.jar")).exists(); + assertThat(tempDir.resolve("plugins/worldguard-bukkit-7.0.7-dist.jar")).exists(); + + } +} \ No newline at end of file diff --git a/src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java b/src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java new file mode 100644 index 00000000..7d353c21 --- /dev/null +++ b/src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java @@ -0,0 +1,71 @@ +package me.itzg.helpers.curseforge; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static me.itzg.helpers.curseforge.ModFileRefResolver.idsFrom; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; +import me.itzg.helpers.curseforge.model.CurseForgeFile; +import me.itzg.helpers.curseforge.model.ModLoaderType; +import me.itzg.helpers.http.SharedFetch.Options; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ModFileRefResolverTest { + + @RegisterExtension + static WireMockExtension wm = WireMockExtension.newInstance() + .options(wireMockConfig() + .dynamicPort() + .usingFilesUnderClasspath("curseforge") + ) + .configureStaticDsl(true) + .build(); + + public static Stream testMcModsRefsArgs() { + return Stream.of( + arguments("238222:4644453", null, null, null, new ModFileIds(238222, 4644453)), + arguments("jei:4644453", CurseForgeApiClient.CATEGORY_MC_MODS, null, null, + new ModFileIds(238222, 4644453) + ), + arguments("jei@11.6.0.1016", CurseForgeApiClient.CATEGORY_MC_MODS, "1.19.2", ModLoaderType.Forge, + new ModFileIds(238222, 4615177) + ), + arguments("https://www.curseforge.com/minecraft/mc-mods/jei/files/4644453", null, null, null, + new ModFileIds(238222, 4644453) + ), + arguments("https://www.curseforge.com/minecraft/mc-mods/jei", null, "1.20.1", ModLoaderType.Fabric, + new ModFileIds(238222, 4644452) + ) + ); + } + + @ParameterizedTest + @MethodSource("testMcModsRefsArgs") + void testMcModsRefs(String ref, String defaultCategory, String gameVersion, ModLoaderType modLoader, ModFileIds expected) { + + final CurseForgeApiClient apiClient = new CurseForgeApiClient(wm.baseUrl(), "testing", Options.builder().build(), + CurseForgeApiClient.MINECRAFT_GAME_ID + ); + + final CategoryInfo categoryInfo = apiClient.loadCategoryInfo( + Arrays.asList(CurseForgeApiClient.CATEGORY_MC_MODS, CurseForgeApiClient.CATEGORY_BUKKIT_PLUGINS)) + .block(); + + final ModFileRefResolver resolver = new ModFileRefResolver(apiClient, categoryInfo); + final List results = resolver.resolveModFiles(Collections.singletonList(ref), defaultCategory, gameVersion, + modLoader + ) + .block(); + + assertThat(results).hasSize(1); + assertThat(idsFrom(results.get(0))).isEqualTo(expected); + } +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/README.md b/src/test/resources/curseforge/mappings/README.md new file mode 100644 index 00000000..bd8c0c9a --- /dev/null +++ b/src/test/resources/curseforge/mappings/README.md @@ -0,0 +1,24 @@ +Without the following args within the test case + +``` + "--api-base-url", wm.baseUrl(), + "--api-key", "test", +``` + +Started Wiremock and a recording + +Set `CF_API_BASE_URL` to "http://localhost:8080" + +Ran the test in IntelliJ with `CF_API_KEY` env var set to a real API key. + +Copied the mappings into this directory + +Removed from each: +- `persistent` +- `scenarioName` +- `requiredScenarioState` +- `insertionIndex` + +Added to the mods/files: +- `"transformers": ["response-template"]` +- Changed the `downloadUrl` values to use a response template placeholder \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/cdn.json b/src/test/resources/curseforge/mappings/cdn.json new file mode 100644 index 00000000..2ea14e8f --- /dev/null +++ b/src/test/resources/curseforge/mappings/cdn.json @@ -0,0 +1,13 @@ +{ + "request": { + "method": "GET", + "urlPathPattern": "/files/.+" + }, + "response": { + "body": "{{request.path.[1]}}", + "headers": { + "Content-Disposition": "attachment; filename=\"{{request.path.[1]}}\"" + }, + "transformers": ["response-template"] + } +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_categories-452fda0d-5d8a-48ad-a86c-b3d73cb7738d.json b/src/test/resources/curseforge/mappings/v1_categories-452fda0d-5d8a-48ad-a86c-b3d73cb7738d.json new file mode 100644 index 00000000..7dcf1deb --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_categories-452fda0d-5d8a-48ad-a86c-b3d73cb7738d.json @@ -0,0 +1,22 @@ +{ + "id" : "452fda0d-5d8a-48ad-a86c-b3d73cb7738d", + "name" : "v1_categories", + "request" : { + "url" : "/v1/categories?gameId=432&classesOnly=true", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"data\":[{\"id\":4546,\"gameId\":432,\"name\":\"Customization\",\"slug\":\"customization\",\"url\":\"https://www.curseforge.com/minecraft/customization\",\"iconUrl\":\"https://media.forgecdn.net/avatars/52/99/636111139136535634.png\",\"dateModified\":\"2018-08-14T16:13:35.533Z\",\"isClass\":true,\"displayIndex\":0},{\"id\":4471,\"gameId\":432,\"name\":\"Modpacks\",\"slug\":\"modpacks\",\"url\":\"https://www.curseforge.com/minecraft/modpacks\",\"iconUrl\":\"https://media.forgecdn.net/avatars/52/100/636111139251397737.png\",\"dateModified\":\"2016-10-03T22:52:05.14Z\",\"isClass\":true,\"displayIndex\":0},{\"id\":5,\"gameId\":432,\"name\":\"Bukkit Plugins\",\"slug\":\"bukkit-plugins\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/443/636162895990633284.png\",\"dateModified\":\"2022-11-10T20:44:39.38Z\",\"isClass\":true,\"displayIndex\":0},{\"id\":12,\"gameId\":432,\"name\":\"Resource Packs\",\"slug\":\"texture-packs\",\"url\":\"https://www.curseforge.com/minecraft/texture-packs\",\"iconUrl\":\"https://media.forgecdn.net/avatars/52/102/636111139761599118.png\",\"dateModified\":\"2020-06-30T17:54:39.063Z\",\"isClass\":true,\"displayIndex\":2},{\"id\":17,\"gameId\":432,\"name\":\"Worlds\",\"slug\":\"worlds\",\"url\":\"https://www.curseforge.com/minecraft/worlds\",\"iconUrl\":\"https://media.forgecdn.net/avatars/52/103/636111139893035422.png\",\"dateModified\":\"2016-10-03T22:53:09.303Z\",\"isClass\":true,\"displayIndex\":3},{\"id\":6,\"gameId\":432,\"name\":\"Mods\",\"slug\":\"mc-mods\",\"url\":\"https://www.curseforge.com/minecraft/mc-mods\",\"iconUrl\":\"https://media.forgecdn.net/avatars/52/101/636111139584399357.png\",\"dateModified\":\"2020-03-28T16:30:35.283Z\",\"isClass\":true,\"displayIndex\":1},{\"id\":4559,\"gameId\":432,\"name\":\"Addons\",\"slug\":\"mc-addons\",\"url\":\"https://www.curseforge.com/minecraft/mc-addons\",\"iconUrl\":\"https://media.forgecdn.net/avatars/53/339/636123919846079516.png\",\"dateModified\":\"2023-07-26T11:51:11.64Z\",\"isClass\":true,\"displayIndex\":0}]}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:55:05 GMT", + "X-Cache" : "Hit from cloudfront", + "Via" : "1.1 848ee9f48eafd6caa6bf5371a2f79f28.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "74RMyH7zI8FuUzcB0s93PUHOnt6YFmAK17EWRikVHuhi9-oznbxEJw==", + "Age" : "120" + } + }, + "uuid" : "452fda0d-5d8a-48ad-a86c-b3d73cb7738d" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_238222_files_4434385-8de5c26d-b14a-4689-815b-448fc69ecc35.json b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4434385-8de5c26d-b14a-4689-815b-448fc69ecc35.json new file mode 100644 index 00000000..1f95c0b1 --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4434385-8de5c26d-b14a-4689-815b-448fc69ecc35.json @@ -0,0 +1,22 @@ +{ + "id" : "8de5c26d-b14a-4689-815b-448fc69ecc35", + "name" : "v1_mods_238222_files_4434385", + "request" : { + "url" : "/v1/mods/238222/files/4434385", + "method" : "GET" + }, + "response" : { + "status" : 200, + "transformers": ["response-template"], + "body" : "{\"data\":{\"id\":4434385,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.18.2-forge-10.2.1.1003.jar\",\"fileName\":\"jei-1.18.2-forge-10.2.1.1003.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"8e5f67196cb3f51d878442a70039030998e514c5\",\"algo\":1},{\"value\":\"39a4aa8d941d516716b98c21b2c51764\",\"algo\":2}],\"fileDate\":\"2023-03-12T20:32:39.963Z\",\"fileLength\":1085200,\"downloadCount\":4426,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.18.2-forge-10.2.1.1003.jar\",\"gameVersions\":[\"1.18.2\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.18.2\",\"gameVersionPadded\":\"0000000001.0000000018.0000000002\",\"gameVersion\":\"1.18.2\",\"gameVersionReleaseDate\":\"2022-02-28T14:23:37.723Z\",\"gameVersionTypeId\":73250},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":2054208865,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1576841514},{\"name\":\"mezz\",\"fingerprint\":393625322},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"assets\",\"fingerprint\":843262602},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424}]}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:57:04 GMT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 22067fb5d7eb764108747a104222f50a.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "xJYQJMO7rs0ke-vSrl-vadIqfw65fxgLTCM8pkSDul7v6lvK8Jc7hg==" + } + }, + "uuid" : "8de5c26d-b14a-4689-815b-448fc69ecc35" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_238222_files_4593548-302af753-b376-4818-86ce-aa930437b4d1.json b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4593548-302af753-b376-4818-86ce-aa930437b4d1.json new file mode 100644 index 00000000..60ba03fa --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4593548-302af753-b376-4818-86ce-aa930437b4d1.json @@ -0,0 +1,22 @@ +{ + "id" : "302af753-b376-4818-86ce-aa930437b4d1", + "name" : "v1_mods_238222_files_4593548", + "request" : { + "url" : "/v1/mods/238222/files/4593548", + "method" : "GET" + }, + "response" : { + "status" : 200, + "transformers": ["response-template"], + "body" : "{\"data\":{\"id\":4593548,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"fileName\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"95ac179f935376235eb35fa6a854b5051590dcfe\",\"algo\":1},{\"value\":\"2ca8e4f60a1f0ff78838a93f5fb8f697\",\"algo\":2}],\"fileDate\":\"2023-06-17T18:36:36.233Z\",\"fileLength\":1085640,\"downloadCount\":25762,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.18.2-forge-10.2.1.1005.jar\",\"gameVersions\":[\"1.18.2\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.18.2\",\"gameVersionPadded\":\"0000000001.0000000018.0000000002\",\"gameVersion\":\"1.18.2\",\"gameVersionReleaseDate\":\"2022-02-28T14:23:37.723Z\",\"gameVersionTypeId\":73250},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1251961014,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1204616540},{\"name\":\"mezz\",\"fingerprint\":85559634},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"assets\",\"fingerprint\":843262602},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424}]}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:57:05 GMT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 4bbf91f2f9edc22eb68408b6405ae452.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "-U_QozQ6RkbueyecOVrehpd-lDvGGeeYOO09o9W38kM4b2EuegpS_g==" + } + }, + "uuid" : "302af753-b376-4818-86ce-aa930437b4d1" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_238222_files_4615177-dee8eca2-74b7-4997-acca-5d4ecec61f0c.json b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4615177-dee8eca2-74b7-4997-acca-5d4ecec61f0c.json new file mode 100644 index 00000000..d5bfec56 --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4615177-dee8eca2-74b7-4997-acca-5d4ecec61f0c.json @@ -0,0 +1,22 @@ +{ + "id" : "dee8eca2-74b7-4997-acca-5d4ecec61f0c", + "name" : "v1_mods_238222_files_4615177", + "request" : { + "url" : "/v1/mods/238222/files/4615177", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"data\":{\"id\":4615177,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.19.2-forge-11.6.0.1016.jar\",\"fileName\":\"jei-1.19.2-forge-11.6.0.1016.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"9a3ab559d678756ca50f49e4ea6487e43068af44\",\"algo\":1},{\"value\":\"05f4ea9c27d3f01d04f8264bcc6e1247\",\"algo\":2}],\"fileDate\":\"2023-06-29T02:51:10.433Z\",\"fileLength\":1090499,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.19.2-forge-11.6.0.1016.jar\",\"gameVersions\":[\"1.19.2\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.19.2\",\"gameVersionPadded\":\"0000000001.0000000019.0000000002\",\"gameVersion\":\"1.19.2\",\"gameVersionReleaseDate\":\"2022-08-05T14:12:22.413Z\",\"gameVersionTypeId\":73407},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1639277599,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":3186904263},{\"name\":\"mezz\",\"fingerprint\":2041176697},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"assets\",\"fingerprint\":3065967970},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424}]}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Sun, 06 Aug 2023 03:30:31 GMT", + "Vary" : "Accept-Encoding", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 ad310b4d7c581c35032fa3fce068e53c.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "AS7MUeaeVdHEX8FUKI8hi58XfpersFHe53qpee2rmaCCTcO3_uhzGA==" + } + }, + "uuid" : "dee8eca2-74b7-4997-acca-5d4ecec61f0c" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_238222_files_4644453-b7ba62e2-168c-4aa0-aaab-92acfed0d285.json b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4644453-b7ba62e2-168c-4aa0-aaab-92acfed0d285.json new file mode 100644 index 00000000..9d7e46af --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_238222_files_4644453-b7ba62e2-168c-4aa0-aaab-92acfed0d285.json @@ -0,0 +1,23 @@ +{ + "id" : "b7ba62e2-168c-4aa0-aaab-92acfed0d285", + "name" : "v1_mods_238222_files_4644453", + "request" : { + "url" : "/v1/mods/238222/files/4644453", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"data\":{\"id\":4644453,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"fileName\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"480813d80c8a32cc3d05950250f9b3390a1d931d\",\"algo\":1},{\"value\":\"227343c41d400cea82e949bd411480af\",\"algo\":2}],\"fileDate\":\"2023-07-14T22:12:02.65Z\",\"fileLength\":1112923,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.20.1-forge-15.2.0.23.jar\",\"gameVersions\":[\"1.20.1\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.20.1\",\"gameVersionPadded\":\"0000000001.0000000020.0000000001\",\"gameVersion\":\"1.20.1\",\"gameVersionReleaseDate\":\"2023-06-12T14:26:38.477Z\",\"gameVersionTypeId\":75125},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1355995934,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1731804188},{\"name\":\"assets\",\"fingerprint\":3290647580},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"mezz\",\"fingerprint\":1720990856},{\"name\":\"pack.mcmeta\",\"fingerprint\":2606738017}]}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Sun, 06 Aug 2023 03:06:40 GMT", + "Vary" : "Accept-Encoding", + "X-Cache" : "Hit from cloudfront", + "Via" : "1.1 995d6494814d695ff2add6899f970080.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "EbCvFo7z9w2pzwOcV6GWHCHaognAej8e6fr4QdO4gk6y3ErSu2aqJQ==", + "Age" : "82" + } + }, + "uuid" : "b7ba62e2-168c-4aa0-aaab-92acfed0d285" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_31054_files_3677516-37c56a78-938f-44ad-ae0c-b155be83c210.json b/src/test/resources/curseforge/mappings/v1_mods_31054_files_3677516-37c56a78-938f-44ad-ae0c-b155be83c210.json new file mode 100644 index 00000000..7c2988fa --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_31054_files_3677516-37c56a78-938f-44ad-ae0c-b155be83c210.json @@ -0,0 +1,22 @@ +{ + "id" : "37c56a78-938f-44ad-ae0c-b155be83c210", + "name" : "v1_mods_31054_files_3677516", + "request" : { + "url" : "/v1/mods/31054/files/3677516", + "method" : "GET" + }, + "response" : { + "status" : 200, + "transformers": ["response-template"], + "body" : "{\"data\":{\"id\":3677516,\"gameId\":432,\"modId\":31054,\"isAvailable\":true,\"displayName\":\"WorldGuard 7.0.7 (MC 1.17.1-1.18.2)\",\"fileName\":\"worldguard-bukkit-7.0.7-dist.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"1f46cd9d227ce28cd4890f55ffd79d2f6d3ce731\",\"algo\":1},{\"value\":\"bc0cd74f6bfc77fc73193f8ac0cce79f\",\"algo\":2}],\"fileDate\":\"2022-03-06T14:03:24.497Z\",\"fileLength\":1168769,\"downloadCount\":284893,\"downloadUrl\":\"{{request.baseUrl}}/files/worldguard-bukkit-7.0.7-dist.jar\",\"gameVersions\":[\"1.18.1\",\"1.17\",\"1.18.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.18.1\",\"gameVersionPadded\":\"0000000001.0000000018.0000000001\",\"gameVersion\":\"1.18.1\",\"gameVersionReleaseDate\":\"2021-12-26T00:00:00Z\",\"gameVersionTypeId\":1},{\"gameVersionName\":\"1.17\",\"gameVersionPadded\":\"0000000001.0000000017\",\"gameVersion\":\"1.17\",\"gameVersionReleaseDate\":\"2021-06-08T00:00:00Z\",\"gameVersionTypeId\":1},{\"gameVersionName\":\"1.18.2\",\"gameVersionPadded\":\"0000000001.0000000018.0000000002\",\"gameVersion\":\"1.18.2\",\"gameVersionReleaseDate\":\"2022-03-02T00:00:00Z\",\"gameVersionTypeId\":1}],\"dependencies\":[{\"modId\":31043,\"relationType\":3}],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":3511285572,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1566757170},{\"name\":\"com\",\"fingerprint\":3047288578},{\"name\":\"defaults\",\"fingerprint\":1739653355},{\"name\":\"migrations\",\"fingerprint\":4042865746},{\"name\":\"plugin.yml\",\"fingerprint\":3504307119},{\"name\":\"org\",\"fingerprint\":2661636969}]}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:57:04 GMT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 b5141080f2dac9506b5156fa7721b41c.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "70sNKJ5vh4BflUTsqKFmBYg5KqXz8vnRxd6fuwsHg72MUHpufzYlng==" + } + }, + "uuid" : "37c56a78-938f-44ad-ae0c-b155be83c210" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_search-2dd68b95-c81b-49ad-829a-5d89c1c1b2c2.json b/src/test/resources/curseforge/mappings/v1_mods_search-2dd68b95-c81b-49ad-829a-5d89c1c1b2c2.json new file mode 100644 index 00000000..39f03b11 --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_search-2dd68b95-c81b-49ad-829a-5d89c1c1b2c2.json @@ -0,0 +1,22 @@ +{ + "id" : "2dd68b95-c81b-49ad-829a-5d89c1c1b2c2", + "name" : "v1_mods_search", + "request" : { + "url" : "/v1/mods/search?gameId=432&slug=worldguard&classId=5", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"data\":[{\"id\":31054,\"gameId\":432,\"name\":\"WorldGuard\",\"slug\":\"worldguard\",\"links\":{\"websiteUrl\":\"https://www.curseforge.com/minecraft/bukkit-plugins/worldguard\",\"wikiUrl\":\"https://worldguard.enginehub.org/en/latest/\",\"issuesUrl\":\"https://github.com/EngineHub/WorldGuard/issues\",\"sourceUrl\":\"https://github.com/EngineHub/WorldGuard\"},\"summary\":\"WorldGuard\",\"status\":4,\"downloadCount\":7334802,\"isFeatured\":false,\"primaryCategoryId\":116,\"categories\":[{\"id\":116,\"gameId\":432,\"name\":\"Anti-Griefing Tools\",\"slug\":\"anti-griefing-tools\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/anti-griefing-tools\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/449/636162917920134408.png\",\"dateModified\":\"2016-12-02T22:09:52.043Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":127,\"gameId\":432,\"name\":\"General\",\"slug\":\"general\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/general\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/456/636162917924196887.png\",\"dateModified\":\"2016-12-02T22:09:52.42Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":125,\"gameId\":432,\"name\":\"Fixes\",\"slug\":\"fixes\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/fixes\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/454/636162917923103138.png\",\"dateModified\":\"2016-12-02T22:09:52.31Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":115,\"gameId\":432,\"name\":\"Admin Tools\",\"slug\":\"admin-tools\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/admin-tools\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/448/636162917918103026.png\",\"dateModified\":\"2016-12-02T22:09:51.92Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":124,\"gameId\":432,\"name\":\"World Editing and Management\",\"slug\":\"world-editing-and-management\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/world-editing-and-management\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/453/636162917922321868.png\",\"dateModified\":\"2016-12-02T22:09:52.247Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5}],\"classId\":5,\"authors\":[{\"id\":6858201,\"name\":\"sk89q\",\"url\":\"https://www.curseforge.com/members/6858201-sk89q?username=sk89q\"},{\"id\":6838603,\"name\":\"me4502\",\"url\":\"https://www.curseforge.com/members/6838603-me4502?username=me4502\"},{\"id\":3597986,\"name\":\"wizjany_\",\"url\":\"https://www.curseforge.com/members/3597986-wizjany_?username=wizjany_\"},{\"id\":6847816,\"name\":\"zmlaoeu\",\"url\":\"https://www.curseforge.com/members/6847816-zmlaoeu?username=zmlaoeu\"}],\"logo\":{\"id\":308041,\"modId\":31054,\"title\":\"637390520722371961.png\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/avatars/thumbnails/308/41/256/256/637390520722371961.png\",\"url\":\"https://media.forgecdn.net/avatars/308/41/637390520722371961.png\"},\"screenshots\":[{\"id\":117768,\"modId\":31054,\"title\":\"Logo\",\"description\":\"

Logo

\\n\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/117/768/310/172/wg_logo.png\",\"url\":\"https://media.forgecdn.net/attachments/117/768/wg_logo.png\"},{\"id\":117769,\"modId\":31054,\"title\":\"Logo\",\"description\":\"

Logo

\\n\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/117/769/310/172/curse_wg.png\",\"url\":\"https://media.forgecdn.net/attachments/117/769/curse_wg.png\"}],\"mainFileId\":4675318,\"latestFiles\":[{\"id\":4591050,\"gameId\":432,\"modId\":31054,\"isAvailable\":true,\"displayName\":\"WorldGuard 7.0.9 Beta 1 (MC 1.20+)\",\"fileName\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"a264db37f64b9dd9d8558254309d6c21f757f86f\",\"algo\":1},{\"value\":\"a00430a59dc238622c049ae5c4312ff4\",\"algo\":2}],\"fileDate\":\"2023-06-16T11:05:18.407Z\",\"fileLength\":1167369,\"downloadCount\":3492,\"downloadUrl\":\"{{request.baseUrl}}/files/worldguard-bukkit-7.0.9-beta1.jar\",\"gameVersions\":[\"1.20\",\"1.19.1\",\"1.19\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.20\",\"gameVersionPadded\":\"0000000001.0000000020\",\"gameVersion\":\"1.20\",\"gameVersionReleaseDate\":\"2023-06-07T00:00:00Z\",\"gameVersionTypeId\":1},{\"gameVersionName\":\"1.19.1\",\"gameVersionPadded\":\"0000000001.0000000019.0000000001\",\"gameVersion\":\"1.19.1\",\"gameVersionReleaseDate\":\"2022-06-28T00:00:00Z\",\"gameVersionTypeId\":1},{\"gameVersionName\":\"1.19\",\"gameVersionPadded\":\"0000000001.0000000019\",\"gameVersion\":\"1.19\",\"gameVersionReleaseDate\":\"2022-06-08T00:00:00Z\",\"gameVersionTypeId\":1}],\"dependencies\":[{\"modId\":31043,\"relationType\":3}],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":4027117918,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1658652158},{\"name\":\"com\",\"fingerprint\":2537888808},{\"name\":\"defaults\",\"fingerprint\":1739653355},{\"name\":\"migrations\",\"fingerprint\":4042865746},{\"name\":\"plugin.yml\",\"fingerprint\":3262289875},{\"name\":\"org\",\"fingerprint\":2661636969}]},{\"id\":4675318,\"gameId\":432,\"modId\":31054,\"isAvailable\":true,\"displayName\":\"WorldGuard 7.0.9 (MC 1.20+)\",\"fileName\":\"worldguard-bukkit-7.0.9-dist.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"42ea9130bd6df8f3157bbbe7949c9bdcff7c0dff\",\"algo\":1},{\"value\":\"70d6418dd6a2e4492a9f18e5f9ecb10c\",\"algo\":2}],\"fileDate\":\"2023-08-01T00:10:42.4Z\",\"fileLength\":1167465,\"downloadCount\":2,\"downloadUrl\":\"{{request.baseUrl}}/files/worldguard-bukkit-7.0.9-dist.jar\",\"gameVersions\":[\"1.20\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.20\",\"gameVersionPadded\":\"0000000001.0000000020\",\"gameVersion\":\"1.20\",\"gameVersionReleaseDate\":\"2023-06-07T00:00:00Z\",\"gameVersionTypeId\":1}],\"dependencies\":[{\"modId\":31043,\"relationType\":3}],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1773012570,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2682575071},{\"name\":\"com\",\"fingerprint\":774107240},{\"name\":\"defaults\",\"fingerprint\":1739653355},{\"name\":\"migrations\",\"fingerprint\":4042865746},{\"name\":\"plugin.yml\",\"fingerprint\":188626114},{\"name\":\"org\",\"fingerprint\":2661636969}]}],\"latestFilesIndexes\":[{\"gameVersion\":\"1.20\",\"fileId\":4675318,\"filename\":\"worldguard-bukkit-7.0.9-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.20\",\"fileId\":4591050,\"filename\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.1\",\"fileId\":4591050,\"filename\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19\",\"fileId\":4591050,\"filename\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.1\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.3\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.4\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.18.1\",\"fileId\":3677516,\"filename\":\"worldguard-bukkit-7.0.7-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.17\",\"fileId\":3677516,\"filename\":\"worldguard-bukkit-7.0.7-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":3677516,\"filename\":\"worldguard-bukkit-7.0.7-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.16\",\"fileId\":3342964,\"filename\":\"worldguard-bukkit-7.0.5-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.14\",\"fileId\":2989116,\"filename\":\"worldguard-bukkit-7.0.3.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.15\",\"fileId\":2989116,\"filename\":\"worldguard-bukkit-7.0.3.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.13\",\"fileId\":2723606,\"filename\":\"worldguard-bukkit-7.0.0.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.12\",\"fileId\":2610618,\"filename\":\"worldguard-bukkit-6.2.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.9\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.10\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.11\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.8\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.8.1\",\"fileId\":881691,\"filename\":\"worldguard-6.1.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.7.2\",\"fileId\":776555,\"filename\":\"worldguard-5.9.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.7.4\",\"fileId\":776555,\"filename\":\"worldguard-5.9.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.6.4\",\"fileId\":776555,\"filename\":\"worldguard-5.9.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.6.2\",\"fileId\":719257,\"filename\":\"worldguard-5.8.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.6.1\",\"fileId\":719257,\"filename\":\"worldguard-5.8.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.5.0\",\"fileId\":706558,\"filename\":\"worldguard-5.7.5.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.5.1\",\"fileId\":706558,\"filename\":\"worldguard-5.7.5.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.5.2\",\"fileId\":706558,\"filename\":\"worldguard-5.7.5.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.4.7\",\"fileId\":668843,\"filename\":\"worldguard-5.7.1.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.4.6\",\"fileId\":668843,\"filename\":\"worldguard-5.7.1.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.2.5\",\"fileId\":586911,\"filename\":\"worldguard-5.5.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1}],\"latestEarlyAccessFilesIndexes\":[],\"dateCreated\":\"2011-08-25T01:04:04.123Z\",\"dateModified\":\"2023-08-01T07:59:49.76Z\",\"dateReleased\":\"2023-08-01T00:10:42.4Z\",\"allowModDistribution\":true,\"gamePopularityRank\":3037,\"isAvailable\":true,\"thumbsUpCount\":0}],\"pagination\":{\"index\":0,\"pageSize\":50,\"resultCount\":1,\"totalCount\":1}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:57:04 GMT", + "X-Cache" : "Hit from cloudfront", + "Via" : "1.1 771067dca4682f83a6c9963c412d66cc.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "Efh3bapd0AS_iI9SMbWOmO8Rz8I8hKA5UhKlvwXL5HZI4kaTXf4ffQ==", + "Age" : "1" + } + }, + "uuid" : "2dd68b95-c81b-49ad-829a-5d89c1c1b2c2" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_search-781cf780-7195-4075-8193-2c997834472c.json b/src/test/resources/curseforge/mappings/v1_mods_search-781cf780-7195-4075-8193-2c997834472c.json new file mode 100644 index 00000000..dcdd8b4a --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_search-781cf780-7195-4075-8193-2c997834472c.json @@ -0,0 +1,22 @@ +{ + "id" : "781cf780-7195-4075-8193-2c997834472c", + "name" : "v1_mods_search", + "request" : { + "url" : "/v1/mods/search?gameId=432&slug=jei&classId=6", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"data\":[{\"id\":238222,\"gameId\":432,\"name\":\"Just Enough Items (JEI)\",\"slug\":\"jei\",\"links\":{\"websiteUrl\":\"https://www.curseforge.com/minecraft/mc-mods/jei\",\"wikiUrl\":\"\",\"issuesUrl\":\"https://github.com/mezz/JustEnoughItems/issues?q=is%3Aissue\",\"sourceUrl\":\"https://github.com/mezz/JustEnoughItems\"},\"summary\":\"View Items and Recipes\",\"status\":4,\"downloadCount\":242375841,\"isFeatured\":false,\"primaryCategoryId\":423,\"categories\":[{\"id\":423,\"gameId\":432,\"name\":\"Map and Information\",\"slug\":\"map-information\",\"url\":\"https://www.curseforge.com/minecraft/mc-mods/map-information\",\"iconUrl\":\"https://media.forgecdn.net/avatars/6/38/635351497437388438.png\",\"dateModified\":\"2014-05-08T17:42:23.74Z\",\"isClass\":false,\"classId\":6,\"parentCategoryId\":6},{\"id\":421,\"gameId\":432,\"name\":\"API and Library\",\"slug\":\"library-api\",\"url\":\"https://www.curseforge.com/minecraft/mc-mods/library-api\",\"iconUrl\":\"https://media.forgecdn.net/avatars/6/36/635351496947765531.png\",\"dateModified\":\"2014-05-23T03:21:44.06Z\",\"isClass\":false,\"classId\":6,\"parentCategoryId\":6}],\"classId\":6,\"authors\":[{\"id\":17072262,\"name\":\"mezz\",\"url\":\"https://www.curseforge.com/members/17072262-mezz?username=mezz\"}],\"logo\":{\"id\":29069,\"modId\":238222,\"title\":\"635838945588716414.jpeg\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/avatars/thumbnails/29/69/256/256/635838945588716414.jpeg\",\"url\":\"https://media.forgecdn.net/avatars/29/69/635838945588716414.jpeg\"},\"screenshots\":[{\"id\":31417,\"modId\":238222,\"title\":\"Recipe Completion\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/417/310/172/thzzdin.png\",\"url\":\"https://media.forgecdn.net/attachments/31/417/thzzdin.png\"},{\"id\":31419,\"modId\":238222,\"title\":\"Potions\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/419/310/172/t7f7jh6.png\",\"url\":\"https://media.forgecdn.net/attachments/31/419/t7f7jh6.png\"},{\"id\":31420,\"modId\":238222,\"title\":\"Itemlist Edit Mode\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/420/310/172/tgafkma.png\",\"url\":\"https://media.forgecdn.net/attachments/31/420/tgafkma.png\"},{\"id\":31418,\"modId\":238222,\"title\":\"Big Screen Support\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/418/310/172/9lngh5f.png\",\"url\":\"https://media.forgecdn.net/attachments/31/418/9lngh5f.png\"}],\"mainFileId\":4593548,\"latestFiles\":[{\"id\":3040523,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei_1.12.2-4.16.1.301.jar\",\"fileName\":\"jei_1.12.2-4.16.1.301.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"3045e8440ea44071d8b83c4e7b3c190348fdc527\",\"algo\":1},{\"value\":\"1dee4be93d666e2228039c551e927b35\",\"algo\":2}],\"fileDate\":\"2020-08-24T01:01:39.123Z\",\"fileLength\":653211,\"downloadCount\":11752168,\"downloadUrl\":\"{{request.baseUrl}}/files/jei_1.12.2-4.16.1.301.jar\",\"gameVersions\":[\"1.12.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.12.2\",\"gameVersionPadded\":\"0000000001.0000000012.0000000002\",\"gameVersion\":\"1.12.2\",\"gameVersionReleaseDate\":\"2017-09-18T05:00:00Z\",\"gameVersionTypeId\":628}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":3089143260,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2236405288},{\"name\":\"mezz\",\"fingerprint\":2222830911},{\"name\":\"pack.mcmeta\",\"fingerprint\":1488642189},{\"name\":\"mcmod.info\",\"fingerprint\":3528499262},{\"name\":\"assets\",\"fingerprint\":9943101}]},{\"id\":3272039,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.13.2-5.0.0.31.jar\",\"fileName\":\"jei-1.13.2-5.0.0.31.jar\",\"releaseType\":3,\"fileStatus\":4,\"hashes\":[{\"value\":\"aa15cdea079db8b91d75e3c68216df80a70545d8\",\"algo\":1},{\"value\":\"1ee1f4fb4c6e199c02c7d15cbd0d2c8a\",\"algo\":2}],\"fileDate\":\"2021-04-11T03:49:47.687Z\",\"fileLength\":690802,\"downloadCount\":8644,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.13.2-5.0.0.31.jar\",\"gameVersions\":[\"1.13.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.13.2\",\"gameVersionPadded\":\"0000000001.0000000013.0000000002\",\"gameVersion\":\"1.13.2\",\"gameVersionReleaseDate\":\"2018-10-22T00:00:00Z\",\"gameVersionTypeId\":55023}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":2700304635,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1102858494},{\"name\":\"mezz\",\"fingerprint\":2811918946},{\"name\":\"pack.mcmeta\",\"fingerprint\":3652707984},{\"name\":\"assets\",\"fingerprint\":88833534}]},{\"id\":4087656,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.19.2-fabric-11.4.0.286.jar\",\"fileName\":\"jei-1.19.2-fabric-11.4.0.286.jar\",\"releaseType\":3,\"fileStatus\":4,\"hashes\":[{\"value\":\"f4c77ecd8b897a12c2c8a26350d93a93322a8bcd\",\"algo\":1},{\"value\":\"cbf23483d172a38b71419100e227f017\",\"algo\":2}],\"fileDate\":\"2022-11-15T02:14:24.907Z\",\"fileLength\":1068892,\"downloadCount\":85246,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.19.2-fabric-11.4.0.286.jar\",\"gameVersions\":[\"Fabric\",\"1.19.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"Fabric\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-09-01T00:00:00Z\",\"gameVersionTypeId\":68441},{\"gameVersionName\":\"1.19.2\",\"gameVersionPadded\":\"0000000001.0000000019.0000000002\",\"gameVersion\":\"1.19.2\",\"gameVersionReleaseDate\":\"2022-08-05T14:12:22.413Z\",\"gameVersionTypeId\":73407}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1613607509,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2822576509},{\"name\":\"jei.accesswidener\",\"fingerprint\":3441454662},{\"name\":\"assets\",\"fingerprint\":224894697},{\"name\":\"fabric.mod.json\",\"fingerprint\":1230897332},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"jei.mixins.json\",\"fingerprint\":623960849},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"jei-1.19.2-fabric-refmap.json\",\"fingerprint\":1412800118},{\"name\":\"mezz\",\"fingerprint\":2346665333}]},{\"id\":4087658,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.19.2-forge-11.4.0.286.jar\",\"fileName\":\"jei-1.19.2-forge-11.4.0.286.jar\",\"releaseType\":3,\"fileStatus\":4,\"hashes\":[{\"value\":\"3bab715ae0f56e1b9a3e1ebfb5e9bb3f677e3711\",\"algo\":1},{\"value\":\"0d458f02d611eafbf29ae36010d03130\",\"algo\":2}],\"fileDate\":\"2022-11-15T02:14:49.103Z\",\"fileLength\":1046401,\"downloadCount\":628343,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.19.2-forge-11.4.0.286.jar\",\"gameVersions\":[\"1.19.2\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.19.2\",\"gameVersionPadded\":\"0000000001.0000000019.0000000002\",\"gameVersion\":\"1.19.2\",\"gameVersionReleaseDate\":\"2022-08-05T14:12:22.413Z\",\"gameVersionTypeId\":73407},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":4135756105,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2624845708},{\"name\":\"mezz\",\"fingerprint\":1839722990},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"assets\",\"fingerprint\":224894697}]},{\"id\":4538010,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei_1.12.2-4.16.1.1003.jar\",\"fileName\":\"jei_1.12.2-4.16.1.1003.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"a4e3b713de6604ee558f30207dd2bde59c9ca21d\",\"algo\":1},{\"value\":\"83e3c785904fdc1bfc70da0264969799\",\"algo\":2}],\"fileDate\":\"2023-05-15T03:15:51.37Z\",\"fileLength\":653678,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei_1.12.2-4.16.1.1003.jar\",\"gameVersions\":[\"1.12.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.12.2\",\"gameVersionPadded\":\"0000000001.0000000012.0000000002\",\"gameVersion\":\"1.12.2\",\"gameVersionReleaseDate\":\"2017-09-18T05:00:00Z\",\"gameVersionTypeId\":628}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1089162688,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":3133722460},{\"name\":\"mezz\",\"fingerprint\":3650387934},{\"name\":\"pack.mcmeta\",\"fingerprint\":1488642189},{\"name\":\"assets\",\"fingerprint\":2844848637},{\"name\":\"mcmod.info\",\"fingerprint\":3200559332}]},{\"id\":4593548,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"fileName\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"95ac179f935376235eb35fa6a854b5051590dcfe\",\"algo\":1},{\"value\":\"2ca8e4f60a1f0ff78838a93f5fb8f697\",\"algo\":2}],\"fileDate\":\"2023-06-17T18:36:36.233Z\",\"fileLength\":1085640,\"downloadCount\":25762,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.18.2-forge-10.2.1.1005.jar\",\"gameVersions\":[\"1.18.2\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.18.2\",\"gameVersionPadded\":\"0000000001.0000000018.0000000002\",\"gameVersion\":\"1.18.2\",\"gameVersionReleaseDate\":\"2022-02-28T14:23:37.723Z\",\"gameVersionTypeId\":73250},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1251961014,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1204616540},{\"name\":\"mezz\",\"fingerprint\":85559634},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"assets\",\"fingerprint\":843262602},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424}]},{\"id\":4644452,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.20.1-fabric-15.2.0.23.jar\",\"fileName\":\"jei-1.20.1-fabric-15.2.0.23.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"159712ea6f234d52f6d051c6beb7436684a57154\",\"algo\":1},{\"value\":\"8abaf346dd011e5cc68477df7866f20b\",\"algo\":2}],\"fileDate\":\"2023-07-14T22:11:46.73Z\",\"fileLength\":1138096,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.20.1-fabric-15.2.0.23.jar\",\"gameVersions\":[\"Fabric\",\"1.20.1\"],\"sortableGameVersions\":[{\"gameVersionName\":\"Fabric\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-09-01T00:00:00Z\",\"gameVersionTypeId\":68441},{\"gameVersionName\":\"1.20.1\",\"gameVersionPadded\":\"0000000001.0000000020.0000000001\",\"gameVersion\":\"1.20.1\",\"gameVersionReleaseDate\":\"2023-06-12T14:26:38.477Z\",\"gameVersionTypeId\":75125}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":4253627375,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2954329278},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"assets\",\"fingerprint\":3290647580},{\"name\":\"pack.mcmeta\",\"fingerprint\":2606738017},{\"name\":\"fabric.mod.json\",\"fingerprint\":3989215860},{\"name\":\"jei.mixins.json\",\"fingerprint\":1640667786},{\"name\":\"jei.accesswidener\",\"fingerprint\":623111814},{\"name\":\"jei-1.20.1-fabric-refmap.json\",\"fingerprint\":1056944492},{\"name\":\"mezz\",\"fingerprint\":2369653267}]},{\"id\":4644453,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"fileName\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"480813d80c8a32cc3d05950250f9b3390a1d931d\",\"algo\":1},{\"value\":\"227343c41d400cea82e949bd411480af\",\"algo\":2}],\"fileDate\":\"2023-07-14T22:12:02.65Z\",\"fileLength\":1112923,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.20.1-forge-15.2.0.23.jar\",\"gameVersions\":[\"1.20.1\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.20.1\",\"gameVersionPadded\":\"0000000001.0000000020.0000000001\",\"gameVersion\":\"1.20.1\",\"gameVersionReleaseDate\":\"2023-06-12T14:26:38.477Z\",\"gameVersionTypeId\":75125},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1355995934,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1731804188},{\"name\":\"assets\",\"fingerprint\":3290647580},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"mezz\",\"fingerprint\":1720990856},{\"name\":\"pack.mcmeta\",\"fingerprint\":2606738017}]}],\"latestFilesIndexes\":[{\"gameVersion\":\"1.20.1\",\"fileId\":4644453,\"filename\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":1},{\"gameVersion\":\"1.20.1\",\"fileId\":4644452,\"filename\":\"jei-1.20.1-fabric-15.2.0.23.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":4},{\"gameVersion\":\"1.19.2\",\"fileId\":4615177,\"filename\":\"jei-1.19.2-forge-11.6.0.1016.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4615176,\"filename\":\"jei-1.19.2-fabric-11.6.0.1016.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.2\",\"fileId\":4593548,\"filename\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"releaseType\":1,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":4593546,\"filename\":\"jei-1.18.2-fabric-10.2.1.1005.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":4},{\"gameVersion\":\"1.19.4\",\"fileId\":4592504,\"filename\":\"jei-1.19.4-forge-13.1.0.15.jar\",\"releaseType\":1,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.4\",\"fileId\":4592503,\"filename\":\"jei-1.19.4-fabric-13.1.0.15.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.19.4\",\"fileId\":4592495,\"filename\":\"jei-1.19.4-forge-13.1.0.14.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.20\",\"fileId\":4581323,\"filename\":\"jei-1.20-forge-14.0.0.11.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":1},{\"gameVersion\":\"1.20\",\"fileId\":4581322,\"filename\":\"jei-1.20-fabric-14.0.0.11.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":4},{\"gameVersion\":\"1.12.2\",\"fileId\":4538010,\"filename\":\"jei_1.12.2-4.16.1.1003.jar\",\"releaseType\":2,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.19.3\",\"fileId\":4455246,\"filename\":\"jei-1.19.3-forge-12.4.0.22.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.3\",\"fileId\":4455245,\"filename\":\"jei-1.19.3-fabric-12.4.0.22.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.2\",\"fileId\":4434393,\"filename\":\"jei-1.18.2-forge-10.2.1.1004.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.16.5\",\"fileId\":4371666,\"filename\":\"jei-1.16.5-7.8.0.1009.jar\",\"releaseType\":1,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.5\",\"fileId\":4371662,\"filename\":\"jei-1.16.5-7.8.0.1008.jar\",\"releaseType\":2,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.17.1\",\"fileId\":4351306,\"filename\":\"jei-1.17.1-8.3.1.1002.jar\",\"releaseType\":2,\"gameVersionTypeId\":73242,\"modLoader\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4087658,\"filename\":\"jei-1.19.2-forge-11.4.0.286.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4087656,\"filename\":\"jei-1.19.2-fabric-11.4.0.286.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.1\",\"fileId\":4060769,\"filename\":\"jei-1.18.1-9.4.1.276.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":4030311,\"filename\":\"jei-1.18.2-forge-10.1.5.272.jar\",\"releaseType\":3,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":4030310,\"filename\":\"jei-1.18.2-fabric-10.1.5.272.jar\",\"releaseType\":3,\"gameVersionTypeId\":73250,\"modLoader\":4},{\"gameVersion\":\"1.19.1\",\"fileId\":3922508,\"filename\":\"jei-1.19.1-forge-11.2.0.244.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.1\",\"fileId\":3922506,\"filename\":\"jei-1.19.1-fabric-11.2.0.244.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.19\",\"fileId\":3903068,\"filename\":\"jei-1.19-forge-11.1.1.239.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19\",\"fileId\":3903066,\"filename\":\"jei-1.19-fabric-11.1.1.239.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.1\",\"fileId\":3723162,\"filename\":\"jei-1.18.1-9.4.1.172.jar\",\"releaseType\":1,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18\",\"fileId\":3550020,\"filename\":\"jei-1.18-9.0.0.40.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.13.2\",\"fileId\":3272039,\"filename\":\"jei-1.13.2-5.0.0.31.jar\",\"releaseType\":3,\"gameVersionTypeId\":55023},{\"gameVersion\":\"1.15.2\",\"fileId\":3272032,\"filename\":\"jei-1.15.2-6.0.3.16.jar\",\"releaseType\":3,\"gameVersionTypeId\":68722},{\"gameVersion\":\"1.16.4\",\"fileId\":3245003,\"filename\":\"jei-1.16.4-7.6.1.74.jar\",\"releaseType\":2,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.3\",\"fileId\":3104018,\"filename\":\"jei-1.16.3-7.6.0.51.jar\",\"releaseType\":2,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.3\",\"fileId\":3071356,\"filename\":\"jei-1.16.3-7.4.0.40.jar\",\"releaseType\":3,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.2\",\"fileId\":3060935,\"filename\":\"jei-1.16.2-7.3.2.28.jar\",\"releaseType\":3,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.12.2\",\"fileId\":3040523,\"filename\":\"jei_1.12.2-4.16.1.301.jar\",\"releaseType\":1,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.14.4\",\"fileId\":3039707,\"filename\":\"jei-1.14.4-6.0.1.30.jar\",\"releaseType\":3,\"gameVersionTypeId\":64806},{\"gameVersion\":\"1.16.1\",\"fileId\":3028697,\"filename\":\"jei-1.16.1-7.0.1.10.jar\",\"releaseType\":3,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.15.1\",\"fileId\":2855456,\"filename\":\"jei-1.15.1-6.0.0.1.jar\",\"releaseType\":3,\"gameVersionTypeId\":68722},{\"gameVersion\":\"1.14.3\",\"fileId\":2738328,\"filename\":\"jei-1.14.3-6.0.0.8.jar\",\"releaseType\":3,\"gameVersionTypeId\":64806},{\"gameVersion\":\"1.14.2\",\"fileId\":2733474,\"filename\":\"jei-1.14.2-6.0.0.3.jar\",\"releaseType\":3,\"gameVersionTypeId\":64806},{\"gameVersion\":\"1.10.2\",\"fileId\":2561516,\"filename\":\"jei_1.10.2-3.14.8.422.jar\",\"releaseType\":2,\"gameVersionTypeId\":572},{\"gameVersion\":\"1.12\",\"fileId\":2485363,\"filename\":\"jei_1.12.2-4.7.11.102.jar\",\"releaseType\":2,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.12.1\",\"fileId\":2485363,\"filename\":\"jei_1.12.2-4.7.11.102.jar\",\"releaseType\":2,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.12\",\"fileId\":2478647,\"filename\":\"jei_1.12.1-4.7.8.95.jar\",\"releaseType\":1,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.12.1\",\"fileId\":2478647,\"filename\":\"jei_1.12.1-4.7.8.95.jar\",\"releaseType\":1,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.11.2\",\"fileId\":2461378,\"filename\":\"jei_1.11.2-4.5.1.296.jar\",\"releaseType\":2,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.11.2\",\"fileId\":2453428,\"filename\":\"jei_1.11.2-4.5.0.294.jar\",\"releaseType\":1,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.12\",\"fileId\":2442204,\"filename\":\"jei_1.12-4.7.0.68.jar\",\"releaseType\":3,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.8.9\",\"fileId\":2431977,\"filename\":\"jei_1.8.9-2.28.18.187.jar\",\"releaseType\":1,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.10.2\",\"fileId\":2428966,\"filename\":\"jei_1.10.2-3.14.7.420.jar\",\"releaseType\":1,\"gameVersionTypeId\":572},{\"gameVersion\":\"1.11\",\"fileId\":2360492,\"filename\":\"jei_1.11-4.1.1.208.jar\",\"releaseType\":2,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.11\",\"fileId\":2350616,\"filename\":\"jei_1.11-4.0.4.199.jar\",\"releaseType\":3,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.9.4\",\"fileId\":2313650,\"filename\":\"jei_1.9.4-3.6.8.225.jar\",\"releaseType\":1,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.10\",\"fileId\":2310912,\"filename\":\"jei_1.10-3.7.1.219.jar\",\"releaseType\":1,\"gameVersionTypeId\":572},{\"gameVersion\":\"1.9.4\",\"fileId\":2306298,\"filename\":\"jei_1.9.4-3.6.2.211.jar\",\"releaseType\":2,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.9\",\"fileId\":2305823,\"filename\":\"jei_1.9.4-3.4.4.208.jar\",\"releaseType\":2,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.9\",\"fileId\":2304545,\"filename\":\"jei_1.9.4-3.4.3.207.jar\",\"releaseType\":1,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.8.9\",\"fileId\":2292565,\"filename\":\"jei_1.8.9-2.28.14.182.jar\",\"releaseType\":2,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8.8\",\"fileId\":2275072,\"filename\":\"jei_1.8.9-2.16.2.78.jar\",\"releaseType\":1,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8\",\"fileId\":2273901,\"filename\":\"jei_1.8-2.14.0.139.jar\",\"releaseType\":1,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8.8\",\"fileId\":2270928,\"filename\":\"jei_1.8.8-2.8.3.39.jar\",\"releaseType\":2,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8\",\"fileId\":2270927,\"filename\":\"jei_1.8-1.8.3.96.jar\",\"releaseType\":2,\"gameVersionTypeId\":4}],\"latestEarlyAccessFilesIndexes\":[],\"dateCreated\":\"2015-11-23T22:55:58.84Z\",\"dateModified\":\"2023-07-14T22:23:01.923Z\",\"dateReleased\":\"2023-07-14T22:12:02.65Z\",\"allowModDistribution\":true,\"gamePopularityRank\":1,\"isAvailable\":true,\"thumbsUpCount\":1}],\"pagination\":{\"index\":0,\"pageSize\":50,\"resultCount\":1,\"totalCount\":1}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:57:04 GMT", + "X-Cache" : "Hit from cloudfront", + "Via" : "1.1 ae39d1ac6bb931d0ff3d636fc3e249de.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "1pyhWDSe72SJ1Rv_vFvKD529TzM9AXV_yxXZ6Iq-K_goPZZsj5MGVw==", + "Age" : "1" + } + }, + "uuid" : "781cf780-7195-4075-8193-2c997834472c" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_search-9857bbc1-5406-4357-9d79-1030a97f7a72.json b/src/test/resources/curseforge/mappings/v1_mods_search-9857bbc1-5406-4357-9d79-1030a97f7a72.json new file mode 100644 index 00000000..680692ad --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_search-9857bbc1-5406-4357-9d79-1030a97f7a72.json @@ -0,0 +1,21 @@ +{ + "id" : "9857bbc1-5406-4357-9d79-1030a97f7a72", + "name" : "v1_mods_search", + "request" : { + "url" : "/v1/mods/search?gameId=432&slug=worldguard&classId=5", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"data\":[{\"id\":31054,\"gameId\":432,\"name\":\"WorldGuard\",\"slug\":\"worldguard\",\"links\":{\"websiteUrl\":\"https://www.curseforge.com/minecraft/bukkit-plugins/worldguard\",\"wikiUrl\":\"https://worldguard.enginehub.org/en/latest/\",\"issuesUrl\":\"https://github.com/EngineHub/WorldGuard/issues\",\"sourceUrl\":\"https://github.com/EngineHub/WorldGuard\"},\"summary\":\"WorldGuard\",\"status\":4,\"downloadCount\":7334802,\"isFeatured\":false,\"primaryCategoryId\":116,\"categories\":[{\"id\":116,\"gameId\":432,\"name\":\"Anti-Griefing Tools\",\"slug\":\"anti-griefing-tools\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/anti-griefing-tools\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/449/636162917920134408.png\",\"dateModified\":\"2016-12-02T22:09:52.043Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":127,\"gameId\":432,\"name\":\"General\",\"slug\":\"general\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/general\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/456/636162917924196887.png\",\"dateModified\":\"2016-12-02T22:09:52.42Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":125,\"gameId\":432,\"name\":\"Fixes\",\"slug\":\"fixes\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/fixes\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/454/636162917923103138.png\",\"dateModified\":\"2016-12-02T22:09:52.31Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":115,\"gameId\":432,\"name\":\"Admin Tools\",\"slug\":\"admin-tools\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/admin-tools\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/448/636162917918103026.png\",\"dateModified\":\"2016-12-02T22:09:51.92Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5},{\"id\":124,\"gameId\":432,\"name\":\"World Editing and Management\",\"slug\":\"world-editing-and-management\",\"url\":\"https://www.curseforge.com/minecraft/bukkit-plugins/world-editing-and-management\",\"iconUrl\":\"https://media.forgecdn.net/avatars/65/453/636162917922321868.png\",\"dateModified\":\"2016-12-02T22:09:52.247Z\",\"isClass\":false,\"classId\":5,\"parentCategoryId\":5}],\"classId\":5,\"authors\":[{\"id\":6858201,\"name\":\"sk89q\",\"url\":\"https://www.curseforge.com/members/6858201-sk89q?username=sk89q\"},{\"id\":6838603,\"name\":\"me4502\",\"url\":\"https://www.curseforge.com/members/6838603-me4502?username=me4502\"},{\"id\":3597986,\"name\":\"wizjany_\",\"url\":\"https://www.curseforge.com/members/3597986-wizjany_?username=wizjany_\"},{\"id\":6847816,\"name\":\"zmlaoeu\",\"url\":\"https://www.curseforge.com/members/6847816-zmlaoeu?username=zmlaoeu\"}],\"logo\":{\"id\":308041,\"modId\":31054,\"title\":\"637390520722371961.png\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/avatars/thumbnails/308/41/256/256/637390520722371961.png\",\"url\":\"https://media.forgecdn.net/avatars/308/41/637390520722371961.png\"},\"screenshots\":[{\"id\":117768,\"modId\":31054,\"title\":\"Logo\",\"description\":\"

Logo

\\n\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/117/768/310/172/wg_logo.png\",\"url\":\"https://media.forgecdn.net/attachments/117/768/wg_logo.png\"},{\"id\":117769,\"modId\":31054,\"title\":\"Logo\",\"description\":\"

Logo

\\n\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/117/769/310/172/curse_wg.png\",\"url\":\"https://media.forgecdn.net/attachments/117/769/curse_wg.png\"}],\"mainFileId\":4675318,\"latestFiles\":[{\"id\":4591050,\"gameId\":432,\"modId\":31054,\"isAvailable\":true,\"displayName\":\"WorldGuard 7.0.9 Beta 1 (MC 1.20+)\",\"fileName\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"a264db37f64b9dd9d8558254309d6c21f757f86f\",\"algo\":1},{\"value\":\"a00430a59dc238622c049ae5c4312ff4\",\"algo\":2}],\"fileDate\":\"2023-06-16T11:05:18.407Z\",\"fileLength\":1167369,\"downloadCount\":3492,\"downloadUrl\":\"{{request.baseUrl}}/files/worldguard-bukkit-7.0.9-beta1.jar\",\"gameVersions\":[\"1.20\",\"1.19.1\",\"1.19\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.20\",\"gameVersionPadded\":\"0000000001.0000000020\",\"gameVersion\":\"1.20\",\"gameVersionReleaseDate\":\"2023-06-07T00:00:00Z\",\"gameVersionTypeId\":1},{\"gameVersionName\":\"1.19.1\",\"gameVersionPadded\":\"0000000001.0000000019.0000000001\",\"gameVersion\":\"1.19.1\",\"gameVersionReleaseDate\":\"2022-06-28T00:00:00Z\",\"gameVersionTypeId\":1},{\"gameVersionName\":\"1.19\",\"gameVersionPadded\":\"0000000001.0000000019\",\"gameVersion\":\"1.19\",\"gameVersionReleaseDate\":\"2022-06-08T00:00:00Z\",\"gameVersionTypeId\":1}],\"dependencies\":[{\"modId\":31043,\"relationType\":3}],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":4027117918,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1658652158},{\"name\":\"com\",\"fingerprint\":2537888808},{\"name\":\"defaults\",\"fingerprint\":1739653355},{\"name\":\"migrations\",\"fingerprint\":4042865746},{\"name\":\"plugin.yml\",\"fingerprint\":3262289875},{\"name\":\"org\",\"fingerprint\":2661636969}]},{\"id\":4675318,\"gameId\":432,\"modId\":31054,\"isAvailable\":true,\"displayName\":\"WorldGuard 7.0.9 (MC 1.20+)\",\"fileName\":\"worldguard-bukkit-7.0.9-dist.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"42ea9130bd6df8f3157bbbe7949c9bdcff7c0dff\",\"algo\":1},{\"value\":\"70d6418dd6a2e4492a9f18e5f9ecb10c\",\"algo\":2}],\"fileDate\":\"2023-08-01T00:10:42.4Z\",\"fileLength\":1167465,\"downloadCount\":2,\"downloadUrl\":\"{{request.baseUrl}}/files/worldguard-bukkit-7.0.9-dist.jar\",\"gameVersions\":[\"1.20\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.20\",\"gameVersionPadded\":\"0000000001.0000000020\",\"gameVersion\":\"1.20\",\"gameVersionReleaseDate\":\"2023-06-07T00:00:00Z\",\"gameVersionTypeId\":1}],\"dependencies\":[{\"modId\":31043,\"relationType\":3}],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1773012570,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2682575071},{\"name\":\"com\",\"fingerprint\":774107240},{\"name\":\"defaults\",\"fingerprint\":1739653355},{\"name\":\"migrations\",\"fingerprint\":4042865746},{\"name\":\"plugin.yml\",\"fingerprint\":188626114},{\"name\":\"org\",\"fingerprint\":2661636969}]}],\"latestFilesIndexes\":[{\"gameVersion\":\"1.20\",\"fileId\":4675318,\"filename\":\"worldguard-bukkit-7.0.9-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.20\",\"fileId\":4591050,\"filename\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.1\",\"fileId\":4591050,\"filename\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19\",\"fileId\":4591050,\"filename\":\"worldguard-bukkit-7.0.9-beta1.jar\",\"releaseType\":2,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.1\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.3\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.19.4\",\"fileId\":4554903,\"filename\":\"worldguard-bukkit-7.0.8-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.18.1\",\"fileId\":3677516,\"filename\":\"worldguard-bukkit-7.0.7-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.17\",\"fileId\":3677516,\"filename\":\"worldguard-bukkit-7.0.7-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":3677516,\"filename\":\"worldguard-bukkit-7.0.7-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.16\",\"fileId\":3342964,\"filename\":\"worldguard-bukkit-7.0.5-dist.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.14\",\"fileId\":2989116,\"filename\":\"worldguard-bukkit-7.0.3.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.15\",\"fileId\":2989116,\"filename\":\"worldguard-bukkit-7.0.3.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.13\",\"fileId\":2723606,\"filename\":\"worldguard-bukkit-7.0.0.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.12\",\"fileId\":2610618,\"filename\":\"worldguard-bukkit-6.2.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.9\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.10\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.11\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.8\",\"fileId\":956770,\"filename\":\"worldguard-6.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.8.1\",\"fileId\":881691,\"filename\":\"worldguard-6.1.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.7.2\",\"fileId\":776555,\"filename\":\"worldguard-5.9.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.7.4\",\"fileId\":776555,\"filename\":\"worldguard-5.9.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.6.4\",\"fileId\":776555,\"filename\":\"worldguard-5.9.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.6.2\",\"fileId\":719257,\"filename\":\"worldguard-5.8.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.6.1\",\"fileId\":719257,\"filename\":\"worldguard-5.8.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.5.0\",\"fileId\":706558,\"filename\":\"worldguard-5.7.5.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.5.1\",\"fileId\":706558,\"filename\":\"worldguard-5.7.5.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.5.2\",\"fileId\":706558,\"filename\":\"worldguard-5.7.5.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.4.7\",\"fileId\":668843,\"filename\":\"worldguard-5.7.1.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.4.6\",\"fileId\":668843,\"filename\":\"worldguard-5.7.1.jar\",\"releaseType\":1,\"gameVersionTypeId\":1},{\"gameVersion\":\"1.2.5\",\"fileId\":586911,\"filename\":\"worldguard-5.5.2.jar\",\"releaseType\":1,\"gameVersionTypeId\":1}],\"latestEarlyAccessFilesIndexes\":[],\"dateCreated\":\"2011-08-25T01:04:04.123Z\",\"dateModified\":\"2023-08-01T07:59:49.76Z\",\"dateReleased\":\"2023-08-01T00:10:42.4Z\",\"allowModDistribution\":true,\"gamePopularityRank\":3037,\"isAvailable\":true,\"thumbsUpCount\":0}],\"pagination\":{\"index\":0,\"pageSize\":50,\"resultCount\":1,\"totalCount\":1}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:57:04 GMT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 f9c7cdbfd821ee3522abb640c0e0a228.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "qLZe3iEATEqFDdOf317fseMVP_NgOxHZ9C8gQjmTgfm1Rph6qA81gw==" + } + }, + "uuid" : "9857bbc1-5406-4357-9d79-1030a97f7a72" +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_search-f693f170-735e-4001-8242-b4c24a1e4e21.json b/src/test/resources/curseforge/mappings/v1_mods_search-f693f170-735e-4001-8242-b4c24a1e4e21.json new file mode 100644 index 00000000..3b7407c0 --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_search-f693f170-735e-4001-8242-b4c24a1e4e21.json @@ -0,0 +1,21 @@ +{ + "id" : "f693f170-735e-4001-8242-b4c24a1e4e21", + "name" : "v1_mods_search", + "request" : { + "url" : "/v1/mods/search?gameId=432&slug=jei&classId=6", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"data\":[{\"id\":238222,\"gameId\":432,\"name\":\"Just Enough Items (JEI)\",\"slug\":\"jei\",\"links\":{\"websiteUrl\":\"https://www.curseforge.com/minecraft/mc-mods/jei\",\"wikiUrl\":\"\",\"issuesUrl\":\"https://github.com/mezz/JustEnoughItems/issues?q=is%3Aissue\",\"sourceUrl\":\"https://github.com/mezz/JustEnoughItems\"},\"summary\":\"View Items and Recipes\",\"status\":4,\"downloadCount\":242375841,\"isFeatured\":false,\"primaryCategoryId\":423,\"categories\":[{\"id\":423,\"gameId\":432,\"name\":\"Map and Information\",\"slug\":\"map-information\",\"url\":\"https://www.curseforge.com/minecraft/mc-mods/map-information\",\"iconUrl\":\"https://media.forgecdn.net/avatars/6/38/635351497437388438.png\",\"dateModified\":\"2014-05-08T17:42:23.74Z\",\"isClass\":false,\"classId\":6,\"parentCategoryId\":6},{\"id\":421,\"gameId\":432,\"name\":\"API and Library\",\"slug\":\"library-api\",\"url\":\"https://www.curseforge.com/minecraft/mc-mods/library-api\",\"iconUrl\":\"https://media.forgecdn.net/avatars/6/36/635351496947765531.png\",\"dateModified\":\"2014-05-23T03:21:44.06Z\",\"isClass\":false,\"classId\":6,\"parentCategoryId\":6}],\"classId\":6,\"authors\":[{\"id\":17072262,\"name\":\"mezz\",\"url\":\"https://www.curseforge.com/members/17072262-mezz?username=mezz\"}],\"logo\":{\"id\":29069,\"modId\":238222,\"title\":\"635838945588716414.jpeg\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/avatars/thumbnails/29/69/256/256/635838945588716414.jpeg\",\"url\":\"https://media.forgecdn.net/avatars/29/69/635838945588716414.jpeg\"},\"screenshots\":[{\"id\":31417,\"modId\":238222,\"title\":\"Recipe Completion\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/417/310/172/thzzdin.png\",\"url\":\"https://media.forgecdn.net/attachments/31/417/thzzdin.png\"},{\"id\":31419,\"modId\":238222,\"title\":\"Potions\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/419/310/172/t7f7jh6.png\",\"url\":\"https://media.forgecdn.net/attachments/31/419/t7f7jh6.png\"},{\"id\":31420,\"modId\":238222,\"title\":\"Itemlist Edit Mode\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/420/310/172/tgafkma.png\",\"url\":\"https://media.forgecdn.net/attachments/31/420/tgafkma.png\"},{\"id\":31418,\"modId\":238222,\"title\":\"Big Screen Support\",\"description\":\"\",\"thumbnailUrl\":\"https://media.forgecdn.net/attachments/thumbnails/31/418/310/172/9lngh5f.png\",\"url\":\"https://media.forgecdn.net/attachments/31/418/9lngh5f.png\"}],\"mainFileId\":4593548,\"latestFiles\":[{\"id\":3040523,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei_1.12.2-4.16.1.301.jar\",\"fileName\":\"jei_1.12.2-4.16.1.301.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"3045e8440ea44071d8b83c4e7b3c190348fdc527\",\"algo\":1},{\"value\":\"1dee4be93d666e2228039c551e927b35\",\"algo\":2}],\"fileDate\":\"2020-08-24T01:01:39.123Z\",\"fileLength\":653211,\"downloadCount\":11752168,\"downloadUrl\":\"{{request.baseUrl}}/files/jei_1.12.2-4.16.1.301.jar\",\"gameVersions\":[\"1.12.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.12.2\",\"gameVersionPadded\":\"0000000001.0000000012.0000000002\",\"gameVersion\":\"1.12.2\",\"gameVersionReleaseDate\":\"2017-09-18T05:00:00Z\",\"gameVersionTypeId\":628}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":3089143260,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2236405288},{\"name\":\"mezz\",\"fingerprint\":2222830911},{\"name\":\"pack.mcmeta\",\"fingerprint\":1488642189},{\"name\":\"mcmod.info\",\"fingerprint\":3528499262},{\"name\":\"assets\",\"fingerprint\":9943101}]},{\"id\":3272039,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.13.2-5.0.0.31.jar\",\"fileName\":\"jei-1.13.2-5.0.0.31.jar\",\"releaseType\":3,\"fileStatus\":4,\"hashes\":[{\"value\":\"aa15cdea079db8b91d75e3c68216df80a70545d8\",\"algo\":1},{\"value\":\"1ee1f4fb4c6e199c02c7d15cbd0d2c8a\",\"algo\":2}],\"fileDate\":\"2021-04-11T03:49:47.687Z\",\"fileLength\":690802,\"downloadCount\":8644,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.13.2-5.0.0.31.jar\",\"gameVersions\":[\"1.13.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.13.2\",\"gameVersionPadded\":\"0000000001.0000000013.0000000002\",\"gameVersion\":\"1.13.2\",\"gameVersionReleaseDate\":\"2018-10-22T00:00:00Z\",\"gameVersionTypeId\":55023}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":2700304635,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1102858494},{\"name\":\"mezz\",\"fingerprint\":2811918946},{\"name\":\"pack.mcmeta\",\"fingerprint\":3652707984},{\"name\":\"assets\",\"fingerprint\":88833534}]},{\"id\":4087656,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.19.2-fabric-11.4.0.286.jar\",\"fileName\":\"jei-1.19.2-fabric-11.4.0.286.jar\",\"releaseType\":3,\"fileStatus\":4,\"hashes\":[{\"value\":\"f4c77ecd8b897a12c2c8a26350d93a93322a8bcd\",\"algo\":1},{\"value\":\"cbf23483d172a38b71419100e227f017\",\"algo\":2}],\"fileDate\":\"2022-11-15T02:14:24.907Z\",\"fileLength\":1068892,\"downloadCount\":85246,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.19.2-fabric-11.4.0.286.jar\",\"gameVersions\":[\"Fabric\",\"1.19.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"Fabric\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-09-01T00:00:00Z\",\"gameVersionTypeId\":68441},{\"gameVersionName\":\"1.19.2\",\"gameVersionPadded\":\"0000000001.0000000019.0000000002\",\"gameVersion\":\"1.19.2\",\"gameVersionReleaseDate\":\"2022-08-05T14:12:22.413Z\",\"gameVersionTypeId\":73407}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1613607509,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2822576509},{\"name\":\"jei.accesswidener\",\"fingerprint\":3441454662},{\"name\":\"assets\",\"fingerprint\":224894697},{\"name\":\"fabric.mod.json\",\"fingerprint\":1230897332},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"jei.mixins.json\",\"fingerprint\":623960849},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"jei-1.19.2-fabric-refmap.json\",\"fingerprint\":1412800118},{\"name\":\"mezz\",\"fingerprint\":2346665333}]},{\"id\":4087658,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.19.2-forge-11.4.0.286.jar\",\"fileName\":\"jei-1.19.2-forge-11.4.0.286.jar\",\"releaseType\":3,\"fileStatus\":4,\"hashes\":[{\"value\":\"3bab715ae0f56e1b9a3e1ebfb5e9bb3f677e3711\",\"algo\":1},{\"value\":\"0d458f02d611eafbf29ae36010d03130\",\"algo\":2}],\"fileDate\":\"2022-11-15T02:14:49.103Z\",\"fileLength\":1046401,\"downloadCount\":628343,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.19.2-forge-11.4.0.286.jar\",\"gameVersions\":[\"1.19.2\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.19.2\",\"gameVersionPadded\":\"0000000001.0000000019.0000000002\",\"gameVersion\":\"1.19.2\",\"gameVersionReleaseDate\":\"2022-08-05T14:12:22.413Z\",\"gameVersionTypeId\":73407},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":4135756105,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2624845708},{\"name\":\"mezz\",\"fingerprint\":1839722990},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"assets\",\"fingerprint\":224894697}]},{\"id\":4538010,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei_1.12.2-4.16.1.1003.jar\",\"fileName\":\"jei_1.12.2-4.16.1.1003.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"a4e3b713de6604ee558f30207dd2bde59c9ca21d\",\"algo\":1},{\"value\":\"83e3c785904fdc1bfc70da0264969799\",\"algo\":2}],\"fileDate\":\"2023-05-15T03:15:51.37Z\",\"fileLength\":653678,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei_1.12.2-4.16.1.1003.jar\",\"gameVersions\":[\"1.12.2\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.12.2\",\"gameVersionPadded\":\"0000000001.0000000012.0000000002\",\"gameVersion\":\"1.12.2\",\"gameVersionReleaseDate\":\"2017-09-18T05:00:00Z\",\"gameVersionTypeId\":628}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1089162688,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":3133722460},{\"name\":\"mezz\",\"fingerprint\":3650387934},{\"name\":\"pack.mcmeta\",\"fingerprint\":1488642189},{\"name\":\"assets\",\"fingerprint\":2844848637},{\"name\":\"mcmod.info\",\"fingerprint\":3200559332}]},{\"id\":4593548,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"fileName\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"releaseType\":1,\"fileStatus\":4,\"hashes\":[{\"value\":\"95ac179f935376235eb35fa6a854b5051590dcfe\",\"algo\":1},{\"value\":\"2ca8e4f60a1f0ff78838a93f5fb8f697\",\"algo\":2}],\"fileDate\":\"2023-06-17T18:36:36.233Z\",\"fileLength\":1085640,\"downloadCount\":25762,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.18.2-forge-10.2.1.1005.jar\",\"gameVersions\":[\"1.18.2\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.18.2\",\"gameVersionPadded\":\"0000000001.0000000018.0000000002\",\"gameVersion\":\"1.18.2\",\"gameVersionReleaseDate\":\"2022-02-28T14:23:37.723Z\",\"gameVersionTypeId\":73250},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1251961014,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1204616540},{\"name\":\"mezz\",\"fingerprint\":85559634},{\"name\":\"pack.mcmeta\",\"fingerprint\":1550930300},{\"name\":\"assets\",\"fingerprint\":843262602},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424}]},{\"id\":4644452,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.20.1-fabric-15.2.0.23.jar\",\"fileName\":\"jei-1.20.1-fabric-15.2.0.23.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"159712ea6f234d52f6d051c6beb7436684a57154\",\"algo\":1},{\"value\":\"8abaf346dd011e5cc68477df7866f20b\",\"algo\":2}],\"fileDate\":\"2023-07-14T22:11:46.73Z\",\"fileLength\":1138096,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.20.1-fabric-15.2.0.23.jar\",\"gameVersions\":[\"Fabric\",\"1.20.1\"],\"sortableGameVersions\":[{\"gameVersionName\":\"Fabric\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-09-01T00:00:00Z\",\"gameVersionTypeId\":68441},{\"gameVersionName\":\"1.20.1\",\"gameVersionPadded\":\"0000000001.0000000020.0000000001\",\"gameVersion\":\"1.20.1\",\"gameVersionReleaseDate\":\"2023-06-12T14:26:38.477Z\",\"gameVersionTypeId\":75125}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":4253627375,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":2954329278},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"assets\",\"fingerprint\":3290647580},{\"name\":\"pack.mcmeta\",\"fingerprint\":2606738017},{\"name\":\"fabric.mod.json\",\"fingerprint\":3989215860},{\"name\":\"jei.mixins.json\",\"fingerprint\":1640667786},{\"name\":\"jei.accesswidener\",\"fingerprint\":623111814},{\"name\":\"jei-1.20.1-fabric-refmap.json\",\"fingerprint\":1056944492},{\"name\":\"mezz\",\"fingerprint\":2369653267}]},{\"id\":4644453,\"gameId\":432,\"modId\":238222,\"isAvailable\":true,\"displayName\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"fileName\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"releaseType\":2,\"fileStatus\":4,\"hashes\":[{\"value\":\"480813d80c8a32cc3d05950250f9b3390a1d931d\",\"algo\":1},{\"value\":\"227343c41d400cea82e949bd411480af\",\"algo\":2}],\"fileDate\":\"2023-07-14T22:12:02.65Z\",\"fileLength\":1112923,\"downloadCount\":0,\"downloadUrl\":\"{{request.baseUrl}}/files/jei-1.20.1-forge-15.2.0.23.jar\",\"gameVersions\":[\"1.20.1\",\"Forge\"],\"sortableGameVersions\":[{\"gameVersionName\":\"1.20.1\",\"gameVersionPadded\":\"0000000001.0000000020.0000000001\",\"gameVersion\":\"1.20.1\",\"gameVersionReleaseDate\":\"2023-06-12T14:26:38.477Z\",\"gameVersionTypeId\":75125},{\"gameVersionName\":\"Forge\",\"gameVersionPadded\":\"0\",\"gameVersion\":\"\",\"gameVersionReleaseDate\":\"2022-10-01T00:00:00Z\",\"gameVersionTypeId\":68441}],\"dependencies\":[],\"alternateFileId\":0,\"isServerPack\":false,\"fileFingerprint\":1355995934,\"modules\":[{\"name\":\"META-INF\",\"fingerprint\":1731804188},{\"name\":\"assets\",\"fingerprint\":3290647580},{\"name\":\"jei-icon.png\",\"fingerprint\":2007185424},{\"name\":\"mezz\",\"fingerprint\":1720990856},{\"name\":\"pack.mcmeta\",\"fingerprint\":2606738017}]}],\"latestFilesIndexes\":[{\"gameVersion\":\"1.20.1\",\"fileId\":4644453,\"filename\":\"jei-1.20.1-forge-15.2.0.23.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":1},{\"gameVersion\":\"1.20.1\",\"fileId\":4644452,\"filename\":\"jei-1.20.1-fabric-15.2.0.23.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":4},{\"gameVersion\":\"1.19.2\",\"fileId\":4615177,\"filename\":\"jei-1.19.2-forge-11.6.0.1016.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4615176,\"filename\":\"jei-1.19.2-fabric-11.6.0.1016.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.2\",\"fileId\":4593548,\"filename\":\"jei-1.18.2-forge-10.2.1.1005.jar\",\"releaseType\":1,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":4593546,\"filename\":\"jei-1.18.2-fabric-10.2.1.1005.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":4},{\"gameVersion\":\"1.19.4\",\"fileId\":4592504,\"filename\":\"jei-1.19.4-forge-13.1.0.15.jar\",\"releaseType\":1,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.4\",\"fileId\":4592503,\"filename\":\"jei-1.19.4-fabric-13.1.0.15.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.19.4\",\"fileId\":4592495,\"filename\":\"jei-1.19.4-forge-13.1.0.14.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.20\",\"fileId\":4581323,\"filename\":\"jei-1.20-forge-14.0.0.11.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":1},{\"gameVersion\":\"1.20\",\"fileId\":4581322,\"filename\":\"jei-1.20-fabric-14.0.0.11.jar\",\"releaseType\":2,\"gameVersionTypeId\":75125,\"modLoader\":4},{\"gameVersion\":\"1.12.2\",\"fileId\":4538010,\"filename\":\"jei_1.12.2-4.16.1.1003.jar\",\"releaseType\":2,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.19.3\",\"fileId\":4455246,\"filename\":\"jei-1.19.3-forge-12.4.0.22.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.3\",\"fileId\":4455245,\"filename\":\"jei-1.19.3-fabric-12.4.0.22.jar\",\"releaseType\":2,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.2\",\"fileId\":4434393,\"filename\":\"jei-1.18.2-forge-10.2.1.1004.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.16.5\",\"fileId\":4371666,\"filename\":\"jei-1.16.5-7.8.0.1009.jar\",\"releaseType\":1,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.5\",\"fileId\":4371662,\"filename\":\"jei-1.16.5-7.8.0.1008.jar\",\"releaseType\":2,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.17.1\",\"fileId\":4351306,\"filename\":\"jei-1.17.1-8.3.1.1002.jar\",\"releaseType\":2,\"gameVersionTypeId\":73242,\"modLoader\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4087658,\"filename\":\"jei-1.19.2-forge-11.4.0.286.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.2\",\"fileId\":4087656,\"filename\":\"jei-1.19.2-fabric-11.4.0.286.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.1\",\"fileId\":4060769,\"filename\":\"jei-1.18.1-9.4.1.276.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":4030311,\"filename\":\"jei-1.18.2-forge-10.1.5.272.jar\",\"releaseType\":3,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18.2\",\"fileId\":4030310,\"filename\":\"jei-1.18.2-fabric-10.1.5.272.jar\",\"releaseType\":3,\"gameVersionTypeId\":73250,\"modLoader\":4},{\"gameVersion\":\"1.19.1\",\"fileId\":3922508,\"filename\":\"jei-1.19.1-forge-11.2.0.244.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19.1\",\"fileId\":3922506,\"filename\":\"jei-1.19.1-fabric-11.2.0.244.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.19\",\"fileId\":3903068,\"filename\":\"jei-1.19-forge-11.1.1.239.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":1},{\"gameVersion\":\"1.19\",\"fileId\":3903066,\"filename\":\"jei-1.19-fabric-11.1.1.239.jar\",\"releaseType\":3,\"gameVersionTypeId\":73407,\"modLoader\":4},{\"gameVersion\":\"1.18.1\",\"fileId\":3723162,\"filename\":\"jei-1.18.1-9.4.1.172.jar\",\"releaseType\":1,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.18\",\"fileId\":3550020,\"filename\":\"jei-1.18-9.0.0.40.jar\",\"releaseType\":2,\"gameVersionTypeId\":73250,\"modLoader\":1},{\"gameVersion\":\"1.13.2\",\"fileId\":3272039,\"filename\":\"jei-1.13.2-5.0.0.31.jar\",\"releaseType\":3,\"gameVersionTypeId\":55023},{\"gameVersion\":\"1.15.2\",\"fileId\":3272032,\"filename\":\"jei-1.15.2-6.0.3.16.jar\",\"releaseType\":3,\"gameVersionTypeId\":68722},{\"gameVersion\":\"1.16.4\",\"fileId\":3245003,\"filename\":\"jei-1.16.4-7.6.1.74.jar\",\"releaseType\":2,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.3\",\"fileId\":3104018,\"filename\":\"jei-1.16.3-7.6.0.51.jar\",\"releaseType\":2,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.3\",\"fileId\":3071356,\"filename\":\"jei-1.16.3-7.4.0.40.jar\",\"releaseType\":3,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.16.2\",\"fileId\":3060935,\"filename\":\"jei-1.16.2-7.3.2.28.jar\",\"releaseType\":3,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.12.2\",\"fileId\":3040523,\"filename\":\"jei_1.12.2-4.16.1.301.jar\",\"releaseType\":1,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.14.4\",\"fileId\":3039707,\"filename\":\"jei-1.14.4-6.0.1.30.jar\",\"releaseType\":3,\"gameVersionTypeId\":64806},{\"gameVersion\":\"1.16.1\",\"fileId\":3028697,\"filename\":\"jei-1.16.1-7.0.1.10.jar\",\"releaseType\":3,\"gameVersionTypeId\":70886,\"modLoader\":1},{\"gameVersion\":\"1.15.1\",\"fileId\":2855456,\"filename\":\"jei-1.15.1-6.0.0.1.jar\",\"releaseType\":3,\"gameVersionTypeId\":68722},{\"gameVersion\":\"1.14.3\",\"fileId\":2738328,\"filename\":\"jei-1.14.3-6.0.0.8.jar\",\"releaseType\":3,\"gameVersionTypeId\":64806},{\"gameVersion\":\"1.14.2\",\"fileId\":2733474,\"filename\":\"jei-1.14.2-6.0.0.3.jar\",\"releaseType\":3,\"gameVersionTypeId\":64806},{\"gameVersion\":\"1.10.2\",\"fileId\":2561516,\"filename\":\"jei_1.10.2-3.14.8.422.jar\",\"releaseType\":2,\"gameVersionTypeId\":572},{\"gameVersion\":\"1.12\",\"fileId\":2485363,\"filename\":\"jei_1.12.2-4.7.11.102.jar\",\"releaseType\":2,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.12.1\",\"fileId\":2485363,\"filename\":\"jei_1.12.2-4.7.11.102.jar\",\"releaseType\":2,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.12\",\"fileId\":2478647,\"filename\":\"jei_1.12.1-4.7.8.95.jar\",\"releaseType\":1,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.12.1\",\"fileId\":2478647,\"filename\":\"jei_1.12.1-4.7.8.95.jar\",\"releaseType\":1,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.11.2\",\"fileId\":2461378,\"filename\":\"jei_1.11.2-4.5.1.296.jar\",\"releaseType\":2,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.11.2\",\"fileId\":2453428,\"filename\":\"jei_1.11.2-4.5.0.294.jar\",\"releaseType\":1,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.12\",\"fileId\":2442204,\"filename\":\"jei_1.12-4.7.0.68.jar\",\"releaseType\":3,\"gameVersionTypeId\":628},{\"gameVersion\":\"1.8.9\",\"fileId\":2431977,\"filename\":\"jei_1.8.9-2.28.18.187.jar\",\"releaseType\":1,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.10.2\",\"fileId\":2428966,\"filename\":\"jei_1.10.2-3.14.7.420.jar\",\"releaseType\":1,\"gameVersionTypeId\":572},{\"gameVersion\":\"1.11\",\"fileId\":2360492,\"filename\":\"jei_1.11-4.1.1.208.jar\",\"releaseType\":2,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.11\",\"fileId\":2350616,\"filename\":\"jei_1.11-4.0.4.199.jar\",\"releaseType\":3,\"gameVersionTypeId\":599},{\"gameVersion\":\"1.9.4\",\"fileId\":2313650,\"filename\":\"jei_1.9.4-3.6.8.225.jar\",\"releaseType\":1,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.10\",\"fileId\":2310912,\"filename\":\"jei_1.10-3.7.1.219.jar\",\"releaseType\":1,\"gameVersionTypeId\":572},{\"gameVersion\":\"1.9.4\",\"fileId\":2306298,\"filename\":\"jei_1.9.4-3.6.2.211.jar\",\"releaseType\":2,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.9\",\"fileId\":2305823,\"filename\":\"jei_1.9.4-3.4.4.208.jar\",\"releaseType\":2,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.9\",\"fileId\":2304545,\"filename\":\"jei_1.9.4-3.4.3.207.jar\",\"releaseType\":1,\"gameVersionTypeId\":552},{\"gameVersion\":\"1.8.9\",\"fileId\":2292565,\"filename\":\"jei_1.8.9-2.28.14.182.jar\",\"releaseType\":2,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8.8\",\"fileId\":2275072,\"filename\":\"jei_1.8.9-2.16.2.78.jar\",\"releaseType\":1,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8\",\"fileId\":2273901,\"filename\":\"jei_1.8-2.14.0.139.jar\",\"releaseType\":1,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8.8\",\"fileId\":2270928,\"filename\":\"jei_1.8.8-2.8.3.39.jar\",\"releaseType\":2,\"gameVersionTypeId\":4},{\"gameVersion\":\"1.8\",\"fileId\":2270927,\"filename\":\"jei_1.8-1.8.3.96.jar\",\"releaseType\":2,\"gameVersionTypeId\":4}],\"latestEarlyAccessFilesIndexes\":[],\"dateCreated\":\"2015-11-23T22:55:58.84Z\",\"dateModified\":\"2023-07-14T22:23:01.923Z\",\"dateReleased\":\"2023-07-14T22:12:02.65Z\",\"allowModDistribution\":true,\"gamePopularityRank\":1,\"isAvailable\":true,\"thumbsUpCount\":1}],\"pagination\":{\"index\":0,\"pageSize\":50,\"resultCount\":1,\"totalCount\":1}}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "Date" : "Fri, 04 Aug 2023 02:57:04 GMT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 ae39d1ac6bb931d0ff3d636fc3e249de.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "DFW57-P1", + "X-Amz-Cf-Id" : "X2UIdUSi1ycHaj3mkHgRwbRsMn-745tVQXnDkkV18VD719bvIETCwQ==" + } + }, + "uuid" : "f693f170-735e-4001-8242-b4c24a1e4e21" +} \ No newline at end of file diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index ad4a60e5..ad27884c 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -27,6 +27,8 @@ + +