Commit 115a6a8b authored by Alberto Scotto's avatar Alberto Scotto
Browse files

Upload: accept an InputStream besides a File

This makes it possible to stream data coming
from another server straight into the POST request to Gitlab,
saving an extra, expensive, and useless IO operation on disk.
parent 37617ec3
package org.gitlab4j.api; package org.gitlab4j.api;
import java.io.File; import java.io.File;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.NotAuthorizedException;
...@@ -382,6 +383,14 @@ public abstract class AbstractApi implements Constants { ...@@ -382,6 +383,14 @@ public abstract class AbstractApi implements Constants {
} }
} }
protected Response upload(Response.Status expectedStatus, String name, InputStream inputStream, String filename, String mediaType, Object... pathArgs) throws GitLabApiException {
try {
return validate(getApiClient().upload(name, inputStream, filename, mediaType, pathArgs), expectedStatus);
} catch (Exception e) {
throw handle(e);
}
}
/** /**
* Perform a file upload with the specified File instance and path objects, returning * Perform a file upload with the specified File instance and path objects, returning
* a ClientResponse instance with the data returned from the endpoint. * a ClientResponse instance with the data returned from the endpoint.
......
...@@ -2,6 +2,7 @@ package org.gitlab4j.api; ...@@ -2,6 +2,7 @@ package org.gitlab4j.api;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.Socket; import java.net.Socket;
import java.net.URL; import java.net.URL;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
...@@ -12,7 +13,6 @@ import java.util.List; ...@@ -12,7 +13,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
...@@ -29,7 +29,6 @@ import javax.ws.rs.core.MediaType; ...@@ -29,7 +29,6 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.StreamingOutput;
import org.gitlab4j.api.Constants.TokenType; import org.gitlab4j.api.Constants.TokenType;
import org.gitlab4j.api.GitLabApi.ApiVersion; import org.gitlab4j.api.GitLabApi.ApiVersion;
import org.gitlab4j.api.utils.JacksonJson; import org.gitlab4j.api.utils.JacksonJson;
...@@ -38,11 +37,13 @@ import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; ...@@ -38,11 +37,13 @@ import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.JerseyClientBuilder; import org.glassfish.jersey.client.JerseyClientBuilder;
import org.glassfish.jersey.media.multipart.BodyPart;
import org.glassfish.jersey.media.multipart.Boundary; import org.glassfish.jersey.media.multipart.Boundary;
import org.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPart; import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
/** /**
...@@ -566,8 +567,7 @@ public class GitLabApiClient implements AutoCloseable { ...@@ -566,8 +567,7 @@ public class GitLabApiClient implements AutoCloseable {
* @throws IOException if an error occurs while constructing the URL * @throws IOException if an error occurs while constructing the URL
*/ */
protected Response upload(String name, File fileToUpload, String mediaTypeString, Object... pathArgs) throws IOException { protected Response upload(String name, File fileToUpload, String mediaTypeString, Object... pathArgs) throws IOException {
URL url = getApiUrl(pathArgs); return upload(name, fileToUpload, mediaTypeString, null, pathArgs);
return (upload(name, fileToUpload, mediaTypeString, null, url));
} }
/** /**
...@@ -600,23 +600,37 @@ public class GitLabApiClient implements AutoCloseable { ...@@ -600,23 +600,37 @@ public class GitLabApiClient implements AutoCloseable {
* @throws IOException if an error occurs while constructing the URL * @throws IOException if an error occurs while constructing the URL
*/ */
protected Response upload(String name, File fileToUpload, String mediaTypeString, Form formData, URL url) throws IOException { protected Response upload(String name, File fileToUpload, String mediaTypeString, Form formData, URL url) throws IOException {
MediaType mediaType = (mediaTypeString != null ? MediaType.valueOf(mediaTypeString) : null);
FileDataBodyPart filePart = mediaType != null ?
new FileDataBodyPart(name, fileToUpload, mediaType) :
new FileDataBodyPart(name, fileToUpload);
return upload(filePart, formData, url);
}
protected Response upload(String name, InputStream inputStream, String filename, String mediaTypeString, Object... pathArgs) throws IOException {
URL url = getApiUrl(pathArgs);
return (upload(name, inputStream, filename, mediaTypeString, null, url));
}
protected Response upload(String name, InputStream inputStream, String filename, String mediaTypeString, Form formData, URL url) throws IOException {
MediaType mediaType = (mediaTypeString != null ? MediaType.valueOf(mediaTypeString) : null); MediaType mediaType = (mediaTypeString != null ? MediaType.valueOf(mediaTypeString) : null);
try (FormDataMultiPart multiPart = new FormDataMultiPart()) { StreamDataBodyPart streamDataBodyPart = mediaType != null ?
new StreamDataBodyPart(name, inputStream, filename, mediaType) :
new StreamDataBodyPart(name, inputStream, filename);
return upload(streamDataBodyPart, formData, url);
}
protected Response upload(BodyPart bodyPart, Form formData, URL url) throws IOException {
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
if (formData != null) { if (formData != null) {
MultivaluedMap<String, String> formParams = formData.asMap(); formData.asMap().forEach((key, values) -> {
formParams.forEach((key, values) -> {
if (values != null) { if (values != null) {
values.forEach(value -> multiPart.field(key, value)); values.forEach(value -> multiPart.field(key, value));
} }
}); });
} }
FileDataBodyPart filePart = mediaType != null ? multiPart.bodyPart(bodyPart);
new FileDataBodyPart(name, fileToUpload, mediaType) :
new FileDataBodyPart(name, fileToUpload);
multiPart.bodyPart(filePart);
final Entity<?> entity = Entity.entity(multiPart, Boundary.addBoundary(multiPart.getMediaType())); final Entity<?> entity = Entity.entity(multiPart, Boundary.addBoundary(multiPart.getMediaType()));
return (invocation(url, null).post(entity)); return (invocation(url, null).post(entity));
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
package org.gitlab4j.api; package org.gitlab4j.api;
import java.io.File; import java.io.File;
import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Date; import java.util.Date;
...@@ -32,12 +33,10 @@ import java.util.Map; ...@@ -32,12 +33,10 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.ws.rs.core.Form; import javax.ws.rs.core.Form;
import javax.ws.rs.core.GenericType; import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.gitlab4j.api.GitLabApi.ApiVersion; import org.gitlab4j.api.GitLabApi.ApiVersion;
import org.gitlab4j.api.models.AccessLevel; import org.gitlab4j.api.models.AccessLevel;
import org.gitlab4j.api.models.AccessRequest; import org.gitlab4j.api.models.AccessRequest;
...@@ -2562,6 +2561,23 @@ public class ProjectApi extends AbstractApi implements Constants { ...@@ -2562,6 +2561,23 @@ public class ProjectApi extends AbstractApi implements Constants {
return (response.readEntity(FileUpload.class)); return (response.readEntity(FileUpload.class));
} }
/**
* Uploads some data in an {@link InputStream} to the specified project,
* to be used in an issue or merge request description, or a comment.
*
* <pre><code>GitLab Endpoint: POST /projects/:id/uploads</code></pre>
*
* @param projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required
* @param inputStream the data to upload, required
* @param mediaType the media type of the file to upload, required
* @return a FileUpload instance with information on the just uploaded file
* @throws GitLabApiException if any exception occurs
*/
public FileUpload uploadFile(Object projectIdOrPath, InputStream inputStream, String filename, String mediaType) throws GitLabApiException {
Response response = upload(Response.Status.CREATED, "file", inputStream, filename, mediaType, "projects", getProjectIdOrPath(projectIdOrPath), "uploads");
return (response.readEntity(FileUpload.class));
}
/** /**
* Get the project's push rules. * Get the project's push rules.
* *
......
package org.gitlab4j.api; package org.gitlab4j.api;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Map; import java.util.Map;
import org.gitlab4j.api.models.FileUpload; import org.gitlab4j.api.models.FileUpload;
import org.gitlab4j.api.models.Project; import org.gitlab4j.api.models.Project;
import org.junit.Before; import org.junit.Before;
...@@ -18,17 +22,17 @@ import org.junit.runners.MethodSorters; ...@@ -18,17 +22,17 @@ import org.junit.runners.MethodSorters;
/** /**
* In order for these tests to run you must set the following properties in test-gitlab4j.properties * In order for these tests to run you must set the following properties in test-gitlab4j.properties
* *
* TEST_NAMESPACE * TEST_NAMESPACE
* TEST_PROJECT_NAME * TEST_PROJECT_NAME
* TEST_HOST_URL * TEST_HOST_URL
* TEST_PRIVATE_TOKEN * TEST_PRIVATE_TOKEN
* *
* If any of the above are NULL, all tests in this class will be skipped. * If any of the above are NULL, all tests in this class will be skipped.
*/ */
@Category(IntegrationTest.class) @Category(IntegrationTest.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestFileUpload extends AbstractIntegrationTest { public class TestUpload extends AbstractIntegrationTest {
// The following needs to be set to your test repository // The following needs to be set to your test repository
private static final String TEST_PROXY_URI = HelperUtils.getProperty("TEST_PROXY_URI"); private static final String TEST_PROXY_URI = HelperUtils.getProperty("TEST_PROXY_URI");
...@@ -37,7 +41,7 @@ public class TestFileUpload extends AbstractIntegrationTest { ...@@ -37,7 +41,7 @@ public class TestFileUpload extends AbstractIntegrationTest {
private static GitLabApi gitLabApi; private static GitLabApi gitLabApi;
public TestFileUpload() { public TestUpload() {
super(); super();
} }
...@@ -93,4 +97,21 @@ public class TestFileUpload extends AbstractIntegrationTest { ...@@ -93,4 +97,21 @@ public class TestFileUpload extends AbstractIntegrationTest {
gitLabApi.close(); gitLabApi.close();
} }
@Test
public void testInputStreamUpload() throws GitLabApiException, FileNotFoundException {
Project project = gitLabApi.getProjectApi().getProject(TEST_NAMESPACE, TEST_PROJECT_NAME);
assertNotNull(project);
String filename = "README.md";
File fileToUpload = new File(filename);
FileUpload fileUpload = gitLabApi.getProjectApi().uploadFile(
project.getId(), new FileInputStream(fileToUpload), filename, null);
assertNotNull(fileUpload);
assertThat(fileUpload.getUrl(), endsWith(filename));
assertThat(fileUpload.getAlt(), equalTo(filename));
}
} }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment