diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2e9c78..a70886c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 17 # Use Java 17 here because the Loom test env needs it + java-version: 21 # Use Java 21 here because the Loom test env needs it distribution: temurin - name: Cache Gradle files @@ -37,7 +37,7 @@ jobs: - name: Test with Gradle run: | - ./gradlew -p fg modrinth + # ./gradlew -p fg modrinth ./gradlew -p loom modrinth if: ${{ !contains(github.event.head_commit.message, 'skip test') }} env: diff --git a/build.gradle b/build.gradle index b100dab..4266d29 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'com.gradle.plugin-publish' version '1.2.0' } -version = '2.8.10' +version = '2.9.0' group = 'com.modrinth.minotaur' base.archivesName = 'Minotaur' description = 'Modrinth plugin for publishing builds to the website!' @@ -19,6 +19,7 @@ dependencies { compileOnly group: 'org.jetbrains', name: 'annotations', version: '+' api group: 'dev.masecla', name: 'Modrinth4J', version: '2.2.0' compileOnly group: 'io.papermc.paperweight', name: 'paperweight-userdev', version: '1.5.2' + compileOnly annotationProcessor(group: 'org.projectlombok', name: 'lombok', version: '1.18.30') } gradlePlugin { diff --git a/loom/build.gradle b/loom/build.gradle index a8cc474..b283775 100644 --- a/loom/build.gradle +++ b/loom/build.gradle @@ -1,5 +1,5 @@ plugins { - id "fabric-loom" version "1.3.+" + id "fabric-loom" version "1.12.+" id "com.modrinth.minotaur" version "2.+" } @@ -13,15 +13,21 @@ dependencies { } java.withSourcesJar() +java.withJavadocJar() import com.modrinth.minotaur.dependencies.DependencyType import com.modrinth.minotaur.dependencies.ModDependency import com.modrinth.minotaur.dependencies.VersionDependency modrinth { - projectId = "mudmod" + projectId = "allergies" uploadFile = remapJar - additionalFiles = [project.file("build/libs/loom-$version-sources.jar")] + additionalFiles = [project.file("build/devlibs/loom-$version-dev.jar")] + additionalFiles { + sourcesJar sourcesJar + javadocJar javadocJar + other 'build/loom-cache/remapped_working/remapped.net.fabricmc-fabric-loader-2b40239f-0.12.12.jar' + } versionType = "alpha" dependencies = [ new ModDependency("test-project", "optional"), @@ -30,9 +36,17 @@ modrinth { dependencies { required.project "corrupted" optional.version "9MsDOrJE" - incompatible.version "mOgUt4GM", "13.0.0" + incompatible.version "AANobbMI", "mc1.21.11-0.8.2-fabric" embedded.project "uiW75cBG" required.version "modmenu", "11.0.2" } debugMode = true } + +tasks.withType(Jar) { + manifest { + attributes([ + 'Timestamp': System.currentTimeMillis(), + ]) + } +} diff --git a/src/main/java/com/modrinth/minotaur/ModrinthExtension.java b/src/main/java/com/modrinth/minotaur/ModrinthExtension.java index f72257a..c00267c 100644 --- a/src/main/java/com/modrinth/minotaur/ModrinthExtension.java +++ b/src/main/java/com/modrinth/minotaur/ModrinthExtension.java @@ -1,8 +1,10 @@ package com.modrinth.minotaur; +import com.modrinth.minotaur.additionalfiles.AdditionalFileDSL; import com.modrinth.minotaur.dependencies.Dependency; import com.modrinth.minotaur.dependencies.container.DependencyDSL; import masecla.modrinth4j.model.version.ProjectVersion.VersionType; +import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; @@ -13,6 +15,7 @@ * {...}} block in the buildscript. */ public class ModrinthExtension extends DependencyDSL { + private final AdditionalFileDSL additionalFileDsl; private final Property apiUrl, token, projectId, versionNumber, versionName, changelog, versionType, syncBodyFrom; private final Property legacyUploadFile; private final RegularFileProperty file; @@ -48,6 +51,7 @@ public class ModrinthExtension extends DependencyDSL { */ public ModrinthExtension(Project project) { super(project.getObjects()); + additionalFileDsl = project.getObjects().newInstance(AdditionalFileDSL.class); apiUrl = project.getObjects().property(String.class).convention(DEFAULT_API_URL); token = project.getObjects().property(String.class).convention(project.getProviders().environmentVariable("MODRINTH_TOKEN")); projectId = project.getObjects().property(String.class); @@ -68,6 +72,14 @@ public ModrinthExtension(Project project) { autoAddDependsOn = project.getObjects().property(Boolean.class).convention(true); } + public void additionalFiles(Action action) { + action.execute(additionalFileDsl); + } + + public AdditionalFileDSL getAdditionalFileDsl() { + return additionalFileDsl; + } + /** * This should not be changed unless you know what you're doing. Its main use case is for debug, development, or * advanced user configurations. diff --git a/src/main/java/com/modrinth/minotaur/TaskModrinthUpload.java b/src/main/java/com/modrinth/minotaur/TaskModrinthUpload.java index 7a3ff7a..c4210e7 100644 --- a/src/main/java/com/modrinth/minotaur/TaskModrinthUpload.java +++ b/src/main/java/com/modrinth/minotaur/TaskModrinthUpload.java @@ -5,7 +5,8 @@ import com.modrinth.minotaur.dependencies.Dependency; import com.modrinth.minotaur.responses.ResponseUpload; import io.papermc.paperweight.userdev.PaperweightUserExtension; -import masecla.modrinth4j.endpoints.version.CreateVersion.CreateVersionRequest; +import com.modrinth.minotaur.masecla.modrinth4j.endpoints.version.TemporaryCreateVersion; +import com.modrinth.minotaur.masecla.modrinth4j.endpoints.version.TemporaryCreateVersion.TemporaryCreateVersionRequest; import masecla.modrinth4j.main.ModrinthAPI; import masecla.modrinth4j.model.version.ProjectVersion; import masecla.modrinth4j.model.version.ProjectVersion.ProjectDependency; @@ -199,8 +200,8 @@ && getProject().getExtensions().findByName("loom") != null) { protoDependencies.stream().map(dependency -> dependency.toNew(api)).forEach(dependencies::add); // Get each of the files, starting with the primary file - List files = new ArrayList<>(); - files.add(ext.getFile().get().getAsFile()); + Map files = new LinkedHashMap<>(); + files.put(ext.getFile().get().getAsFile(), "primary"); // Convert each of the Object files from the extension to a proper File ext.getAdditionalFiles().get().forEach(file -> { @@ -211,11 +212,28 @@ && getProject().getExtensions().findByName("loom") != null) { throw new GradleException("The upload file is missing or null. " + file); } - files.add(resolvedFile); + String fileName = resolvedFile.getName(); + String fileType = null; + + // No switches in Java 8 :( + if (fileName.contains("-dev.jar")) { + fileType = "dev-jar"; + } else if (fileName.contains("-sources.jar")) { + fileType = "sources-jar"; + } else if (fileName.contains("-javadoc.jar")) { + fileType = "javadoc-jar"; + } else if (fileName.contains("asc") || fileName.contains("gpg") || fileName.contains("sig")) { + fileType = "signature"; + } + + files.put(resolvedFile, fileType); }); + ext.getAdditionalFileDsl().getNamedAdditionalFilesAsList().forEach(file -> + files.put(file.getFile().getAsFile(), file.getAdditionalFileType().toString())); + // Start construction of the actual request! - CreateVersionRequest data = CreateVersionRequest.builder() + TemporaryCreateVersionRequest data = TemporaryCreateVersionRequest.builder() .projectId(id) .versionNumber(versionNumber) .name(ext.getVersionName().get()) @@ -236,7 +254,8 @@ && getProject().getExtensions().findByName("loom") != null) { } // Execute the request - ProjectVersion version = api.versions().createProjectVersion(data).join(); + ProjectVersion version = new TemporaryCreateVersion(getProject()).sendRequest(data).join(); + //ProjectVersion version = api.versions().createProjectVersion(data).join(); newVersion = version; //noinspection deprecation uploadInfo = new ResponseUpload(version); diff --git a/src/main/java/com/modrinth/minotaur/Util.java b/src/main/java/com/modrinth/minotaur/Util.java index 16fe99f..d30a30c 100644 --- a/src/main/java/com/modrinth/minotaur/Util.java +++ b/src/main/java/com/modrinth/minotaur/Util.java @@ -16,7 +16,7 @@ * Internal utility methods to make things easier and deduplicated */ @ApiStatus.Internal -class Util { +public class Util { /** * @param project Gradle project for getting various info from * @return A valid {@link ModrinthAPI} instance @@ -49,7 +49,7 @@ static ModrinthAPI api(Project project) { * @param project Gradle project for getting various info from * @return The {@link ModrinthExtension} for the project */ - static ModrinthExtension ext(Project project) { + public static ModrinthExtension ext(Project project) { return project.getExtensions().getByType(ModrinthExtension.class); } @@ -59,7 +59,7 @@ static ModrinthExtension ext(Project project) { * @param project The Gradle project to resolve the extension and version from * @return The extension version number if set; otherwise, the Gradle project version. */ - static String resolveVersionNumber(Project project) { + public static String resolveVersionNumber(Project project) { ModrinthExtension ext = ext(project); if (ext.getVersionNumber().getOrNull() == null) { ext.getVersionNumber().set(project.getVersion().toString()); @@ -100,7 +100,7 @@ static File resolveFile(Project project, Object in) { return project.file(in); } - static Provider resolveFileProperty(Project project, Object in) { + public static Provider resolveFileProperty(Project project, Object in) { if (in == null) { // If input is null we can't really do anything... return project.getObjects().fileProperty(); diff --git a/src/main/java/com/modrinth/minotaur/additionalfiles/AdditionalFileDSL.java b/src/main/java/com/modrinth/minotaur/additionalfiles/AdditionalFileDSL.java new file mode 100644 index 0000000..57524ec --- /dev/null +++ b/src/main/java/com/modrinth/minotaur/additionalfiles/AdditionalFileDSL.java @@ -0,0 +1,100 @@ +package com.modrinth.minotaur.additionalfiles; + +import com.modrinth.minotaur.Util; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Project; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +/** + * the Nested AdditionalFiles configuration + */ +public class AdditionalFileDSL { + private final Project project; + private final NamedDomainObjectContainer additionalFiles; + + /** + * Instantiates a new additionalFiles configuration. + * + * @param project Project + */ + @Inject + public AdditionalFileDSL(final Project project) { + this.project = project; + this.additionalFiles = project.getObjects().domainObjectContainer(NamedAdditionalFile.class); + } + + /** + * Returns the complete NamedAdditionalFile container set mapped and collected as a {@literal List} + * + * @return {@literal List} + */ + public List getNamedAdditionalFilesAsList() { + return new ArrayList<>(this.additionalFiles); + } + + /** + * Creates a required resource pack AdditionalFile Container + * + * @param file the file + */ + public void requiredResourcePack(final Object file) { + this.additionalFiles.add(new NamedAdditionalFile(AdditionalFileType.REQUIRED_RESOURCE_PACK, Util.resolveFileProperty(project, file).get())); + } + + /** + * Creates an optional resource pack AdditionalFile Container + * + * @param file the file + */ + public void optionalResourcePack(final Object file) { + this.additionalFiles.add(new NamedAdditionalFile(AdditionalFileType.OPTIONAL_RESOURCE_PACK, Util.resolveFileProperty(project, file).get())); + } + + /** + * Creates a sources JAR AdditionalFile Container + * + * @param file the file + */ + public void sourcesJar(final Object file) { + this.additionalFiles.add(new NamedAdditionalFile(AdditionalFileType.SOURCES_JAR, Util.resolveFileProperty(project, file).get())); + } + + /** + * Creates a dev JAR AdditionalFile Container + * + * @param file the file + */ + public void devJar(final Object file) { + this.additionalFiles.add(new NamedAdditionalFile(AdditionalFileType.DEV_JAR, Util.resolveFileProperty(project, file).get())); + } + + /** + * Creates a Javadoc JAR AdditionalFile Container + * + * @param file the file + */ + public void javadocJar(final Object file) { + this.additionalFiles.add(new NamedAdditionalFile(AdditionalFileType.JAVADOC_JAR, Util.resolveFileProperty(project, file).get())); + } + + /** + * Creates a signature AdditionalFile Container + * + * @param file the file + */ + public void signature(final Object file) { + this.additionalFiles.add(new NamedAdditionalFile(AdditionalFileType.SIGNATURE, Util.resolveFileProperty(project, file).get())); + } + + /** + * Creates another AdditionalFile Container + * + * @param file the file + */ + public void other(final Object file) { + this.additionalFiles.add(new NamedAdditionalFile(AdditionalFileType.OTHER, Util.resolveFileProperty(project, file).get())); + } +} diff --git a/src/main/java/com/modrinth/minotaur/additionalfiles/AdditionalFileType.java b/src/main/java/com/modrinth/minotaur/additionalfiles/AdditionalFileType.java new file mode 100644 index 0000000..34aa7c8 --- /dev/null +++ b/src/main/java/com/modrinth/minotaur/additionalfiles/AdditionalFileType.java @@ -0,0 +1,63 @@ +package com.modrinth.minotaur.additionalfiles; + +import com.google.gson.annotations.SerializedName; + +import java.util.Locale; + +/** + * The enum representing the additional file types supported by Modrinth. + */ +public enum AdditionalFileType { + /** + * The file is a resource pack file that must be used alongside the file. Primarily meant for data + * pack projects. + */ + @SerializedName("required-resource-pack") + REQUIRED_RESOURCE_PACK, + + /** + * The file is a resource pack file that can be used alongside the file. Primarily meant for data + * pack projects. + */ + @SerializedName("optional-resource-pack") + OPTIONAL_RESOURCE_PACK, + + /** + * The file is a JAR containing source code. + */ + @SerializedName("sources-jar") + SOURCES_JAR, + + /** + * The file is a JAR containing unmapped or development code. + */ + @SerializedName("dev-jar") + DEV_JAR, + + /** + * The file is a JAR containing Javadoc documentation. + */ + @SerializedName("javadoc-jar") + JAVADOC_JAR, + + /** + * The file is a signature file (asc, gpg, or sig). + */ + @SerializedName("signature") + SIGNATURE, + + /** + * The file is a different type of additional file. + */ + @SerializedName("") + OTHER; + + public String toString() { + if (this == OTHER) { + return null; + } else { + return this.name().toLowerCase(Locale.ROOT).replace('_', '-'); + } + } + +} diff --git a/src/main/java/com/modrinth/minotaur/additionalfiles/NamedAdditionalFile.java b/src/main/java/com/modrinth/minotaur/additionalfiles/NamedAdditionalFile.java new file mode 100644 index 0000000..a9e6139 --- /dev/null +++ b/src/main/java/com/modrinth/minotaur/additionalfiles/NamedAdditionalFile.java @@ -0,0 +1,46 @@ +package com.modrinth.minotaur.additionalfiles; + +import org.gradle.api.Named; +import org.gradle.api.file.RegularFile; +import org.jetbrains.annotations.NotNull; + +/** + * Defines a Named AdditionalFile for our NamedAdditionalFileContainer. + */ +public class NamedAdditionalFile implements Named { + private final AdditionalFileType additionalFileType; + private final RegularFile file; + + /** + * Instantiates a new NamedAdditionalFile. + * + * @param additionalFileType the AdditionalFileType + * @param file the file to upload + */ + protected NamedAdditionalFile(AdditionalFileType additionalFileType, RegularFile file) { + this.additionalFileType = additionalFileType; + this.file = file; + } + + /** + * @return the file name + */ + @NotNull + @Override + public String getName() { + return this.file.getAsFile().getName(); + } + + public RegularFile getFile() { + return file; + } + + /** + * Gets the AdditionalFileType as String. + * + * @return the type + */ + public AdditionalFileType getAdditionalFileType() { + return this.additionalFileType; + } +} diff --git a/src/main/java/com/modrinth/minotaur/masecla/modrinth4j/endpoints/version/TemporaryCreateVersion.java b/src/main/java/com/modrinth/minotaur/masecla/modrinth4j/endpoints/version/TemporaryCreateVersion.java new file mode 100644 index 0000000..9b64015 --- /dev/null +++ b/src/main/java/com/modrinth/minotaur/masecla/modrinth4j/endpoints/version/TemporaryCreateVersion.java @@ -0,0 +1,285 @@ +/* + * Copyright 2023 TeamMT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.modrinth.minotaur.masecla.modrinth4j.endpoints.version; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.time.Instant; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; + +import com.modrinth.minotaur.ModrinthExtension; +import com.modrinth.minotaur.Util; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.SneakyThrows; +import masecla.modrinth4j.client.HttpClient; +import masecla.modrinth4j.client.agent.UserAgent; +import masecla.modrinth4j.client.instances.RatelimitedHttpClient; +import masecla.modrinth4j.endpoints.generic.Endpoint; +import masecla.modrinth4j.model.adapters.ISOTimeAdapter; +import masecla.modrinth4j.model.project.ProjectStatus; +import masecla.modrinth4j.model.search.FacetCollection; +import masecla.modrinth4j.model.team.ModrinthPermissionMask; +import masecla.modrinth4j.model.version.ProjectVersion; +import masecla.modrinth4j.model.version.ProjectVersion.ProjectDependency; +import masecla.modrinth4j.model.version.ProjectVersion.VersionType; +import okhttp3.MultipartBody; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.gradle.api.Project; + +/** + * This endpoint is used to create a new version. + */ +public class TemporaryCreateVersion extends Endpoint { + /** + * This class is used to represent the request. + */ + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class TemporaryCreateVersionRequest { + /** The name of the version */ + @NonNull + private String name; + + /** The version number of the version */ + @NonNull + private String versionNumber; + + /** The changelog of the version */ + @NonNull + private String changelog; + + /** The dependencies of the version */ + @Default + private List dependencies = new ArrayList<>(); + /** The game versions of the version */ + private List gameVersions; + /** The type of the version */ + private VersionType versionType; + + /** The loaders of the version */ + @NonNull + private List loaders; + + /** If the version is featured */ + private boolean featured; + + /** The project status */ + private ProjectStatus status; + + /** The requested status of the project */ + private ProjectStatus requestedStatus; + + /** The project ID of the version */ + @NonNull + private String projectId; + + /** The primary file of the version */ + private String primaryFile; + + /** The file types of the version's additional files */ + private Map fileTypes; + + /** The files of the version */ + @NonNull + private transient List fileNames; + + /** The file streams of the version */ + @NonNull + private transient List fileStreams; + + /** + * This class is used to build the request. + */ + public static class TemporaryCreateVersionRequestBuilder { + /** + * This method is used to add files to the request. + * + * @param files - The files to add. + * @return - The builder. + */ + @SneakyThrows + public TemporaryCreateVersionRequestBuilder files(Map files) { + this.fileNames = new ArrayList<>(); + this.fileStreams = new ArrayList<>(); + this.fileTypes = new LinkedHashMap<>(); + + for (Map.Entry entry : files.entrySet()) { + File file = entry.getKey(); + String fileType = entry.getValue(); + + this.fileNames.add(file.getName()); + this.fileStreams.add(new FileInputStream(file)); + if (fileType != null && !fileType.equals("primary")) { + this.fileTypes.put(file.getName(), fileType); + } + } + + return this; + } + + /** + * This method is used to add files to the request. + * + * @param files - The files to add. + * @return - The builder. + */ + @SneakyThrows + public TemporaryCreateVersionRequestBuilder files(File... files) { + this.fileNames = new ArrayList<>(); + this.fileStreams = new ArrayList<>(); + + for (int i = 0; i < files.length; i++) { + this.fileNames.add(files[i].getName()); + this.fileStreams.add(new FileInputStream(files[i])); + } + return this; + } + + /** + * This method is used to add files to the request. + * + * @param files - The files to add. + * @return - The builder. + */ + public TemporaryCreateVersionRequestBuilder files(List files) { + return this.files(files.toArray(new File[files.size()])); + } + } + } + + /** + * This constructor is used to create a new instance of the endpoint. + */ + public TemporaryCreateVersion(Project project) { + super(httpClient(project), new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .registerTypeAdapter(FacetCollection.class, new FacetCollection.FacetAdapter()) + .registerTypeAdapter(ModrinthPermissionMask.class, new ModrinthPermissionMask.ModrinthPermissionMaskAdapter()) + .registerTypeAdapter(Instant.class, new ISOTimeAdapter()) + .create()); + } + + private static HttpClient httpClient(Project project) { + ModrinthExtension ext = Util.ext(project); + String url = ext.getApiUrl().get(); + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + + UserAgent agent = UserAgent.builder() + .authorUsername("modrinth") + .projectName("minotaur") + .projectVersion(Util.class.getPackage().getImplementationVersion()) + .contact(ext.getProjectId().get() + "/" + Util.resolveVersionNumber(project)) + .build(); + + String token = ext.getToken().get(); + if (token.startsWith("mra")) { + throw new RuntimeException("Token must be a personal-access token, not a session token!"); + } else if (!token.startsWith("mrp")) { + project.getLogger().warn("Using GitHub tokens for authentication is deprecated. Please begin to use personal-access tokens."); + } + + return new RatelimitedHttpClient(agent, url, token); + } + + /** + * Returns the endpoint. + */ + @Override + public String getEndpoint() { + return "/version"; + } + + /** + * This method will send the request. + */ + @Override + public CompletableFuture sendRequest(TemporaryCreateVersionRequest request, Map urlParams) { + String url = getReplacedUrl(request, urlParams); + return getClient().connect(url).thenApply(c -> { + JsonObject jsonObject = getGson().toJsonTree(request).getAsJsonObject(); + + if (request.getFileNames().size() > 1) { + String primaryFile = request.getPrimaryFile(); + if (primaryFile == null || primaryFile.isEmpty()) { + primaryFile = request.getFileNames().get(0); + } + jsonObject.addProperty("primary_file", primaryFile); + } + + JsonArray array = new JsonArray(); + for (String filename : request.getFileNames()) { + array.add(filename); + } + jsonObject.add("file_parts", array); + + MultipartBody.Builder body = new MultipartBody.Builder() + .addFormDataPart("data", getGson().toJson(jsonObject)); + + for (int i = 0; i < request.getFileNames().size(); i++) { + body.addFormDataPart(request.getFileNames().get(i), request.getFileNames().get(i), + RequestBody.create(this.readStream(request.getFileStreams().get(i)))); + } + + c.post(body.build()); + + Response response = executeRequest(c); + ProjectVersion version = this.checkBodyForErrors(response.body()); + return version; + }); + } + + /** + * Returns the request class to use. + */ + @Override + public TypeToken getRequestClass() { + return TypeToken.get(TemporaryCreateVersionRequest.class); + } + + /** + * Returns the response class to use. + */ + @Override + public TypeToken getResponseClass() { + return TypeToken.get(ProjectVersion.class); + } + + /** + * Returns the method to use. + */ + @Override + public String getMethod() { + return "POST"; + } +}