Commit 372a1542 authored by Max Tuni's avatar Max Tuni Committed by Tim Olshansky

File upload support refs #167 (#170)

* fixes #167

* fixes #167

* fix author and imports

* fix spacing

* replace tabs with spaces

* better formatting
parent 83b57e90
package org.gitlab.api; package org.gitlab.api;
import com.fasterxml.jackson.databind.DeserializationFeature; import java.io.File;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.gitlab.api.http.GitlabHTTPRequestor;
import org.gitlab.api.http.Query;
import org.gitlab.api.models.*;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
...@@ -18,6 +13,20 @@ import java.util.Arrays; ...@@ -18,6 +13,20 @@ import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.gitlab.api.http.GitlabHTTPRequestor;
import org.gitlab.api.http.Query;
import org.gitlab.api.models.*;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.gitlab.api.http.GitlabHTTPRequestor;
import org.gitlab.api.http.Query;
import org.gitlab.api.models.*;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/** /**
* Gitlab API Wrapper class * Gitlab API Wrapper class
...@@ -621,6 +630,19 @@ public class GitlabAPI { ...@@ -621,6 +630,19 @@ public class GitlabAPI {
return retrieve().getAll(tailUrl, GitlabProject[].class); return retrieve().getAll(tailUrl, GitlabProject[].class);
} }
/**
* Uploads a file to a project
*
* @param project
* @param file
* @return
* @throws IOException
*/
public GitlabUpload uploadFile(GitlabProject project, File file) throws IOException {
String tailUrl = GitlabProject.URL + "/" + sanitizeProjectId(project.getId()) + GitlabUpload.URL;
return dispatch().withAttachment("file", file).to(tailUrl, GitlabUpload.class);
}
/** /**
* *
* Gets a list of a project's builds in Gitlab * Gets a list of a project's builds in Gitlab
......
package org.gitlab.api.http; package org.gitlab.api.http;
import org.apache.commons.io.IOUtils; import java.io.File;
import org.gitlab.api.AuthMethod;
import org.gitlab.api.GitlabAPI;
import org.gitlab.api.GitlabAPIException;
import org.gitlab.api.TokenType;
import org.gitlab.api.models.GitlabCommit;
import javax.net.ssl.*;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
...@@ -19,6 +17,15 @@ import java.util.regex.Matcher; ...@@ -19,6 +17,15 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import javax.net.ssl.*;
import org.apache.commons.io.IOUtils;
import org.gitlab.api.AuthMethod;
import org.gitlab.api.GitlabAPI;
import org.gitlab.api.GitlabAPIException;
import org.gitlab.api.TokenType;
import org.gitlab.api.models.GitlabCommit;
/** /**
* Gitlab HTTP Requestor * Gitlab HTTP Requestor
* Responsible for handling HTTP requests to the Gitlab API * Responsible for handling HTTP requests to the Gitlab API
...@@ -33,6 +40,7 @@ public class GitlabHTTPRequestor { ...@@ -33,6 +40,7 @@ public class GitlabHTTPRequestor {
private String method = "GET"; // Default to GET requests private String method = "GET"; // Default to GET requests
private Map<String, Object> data = new HashMap<String, Object>(); private Map<String, Object> data = new HashMap<String, Object>();
private Map<String, File> attachments = new HashMap<String, File>();
private String apiToken; private String apiToken;
private TokenType tokenType; private TokenType tokenType;
...@@ -108,6 +116,21 @@ public class GitlabHTTPRequestor { ...@@ -108,6 +116,21 @@ public class GitlabHTTPRequestor {
return this; return this;
} }
/**
* Sets the HTTP Form Post parameters for the request
* Has a fluent api for method chaining
*
* @param key Form parameter Key
* @param value Form parameter Value
* @return this
*/
public GitlabHTTPRequestor withAttachment(String key, File file) {
if (file != null && key != null) {
attachments.put(key, file);
}
return this;
}
public <T> T to(String tailAPIUrl, T instance) throws IOException { public <T> T to(String tailAPIUrl, T instance) throws IOException {
return to(tailAPIUrl, null, instance); return to(tailAPIUrl, null, instance);
} }
...@@ -131,8 +154,9 @@ public class GitlabHTTPRequestor { ...@@ -131,8 +154,9 @@ public class GitlabHTTPRequestor {
HttpURLConnection connection = null; HttpURLConnection connection = null;
try { try {
connection = setupConnection(root.getAPIUrl(tailAPIUrl)); connection = setupConnection(root.getAPIUrl(tailAPIUrl));
if (hasAttachments()) {
if (hasOutput()) { submitAttachments(connection);
} else if (hasOutput()) {
submitData(connection); submitData(connection);
} else if ("PUT".equals(method)) { } else if ("PUT".equals(method)) {
// PUT requires Content-Length: 0 even when there is no body (eg: API for protecting a branch) // PUT requires Content-Length: 0 even when there is no body (eg: API for protecting a branch)
...@@ -270,12 +294,55 @@ public class GitlabHTTPRequestor { ...@@ -270,12 +294,55 @@ public class GitlabHTTPRequestor {
}; };
} }
private void submitAttachments(HttpURLConnection connection) throws IOException {
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
String charset = "UTF-8";
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
OutputStream output = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
try {
for (Map.Entry<String, Object> paramEntry : data.entrySet()) {
String paramName = paramEntry.getKey();
String param = GitlabAPI.MAPPER.writeValueAsString(paramEntry.getValue());
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"" + paramName + "\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
writer.append(CRLF).append(param).append(CRLF).flush();
}
for (Map.Entry<String, File> attachMentEntry : attachments.entrySet()) {
File binaryFile = attachMentEntry.getValue();
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\""+ attachMentEntry.getKey() +"\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
writer.append("Content-Transfer-Encoding: binary").append(CRLF);
writer.append(CRLF).flush();
Reader fileReader = new FileReader(binaryFile);
try {
IOUtils.copy(fileReader, output);
} finally {
fileReader.close();
}
output.flush(); // Important before continuing with writer!
writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
}
writer.append("--" + boundary + "--").append(CRLF).flush();
} finally {
writer.close();
}
}
private void submitData(HttpURLConnection connection) throws IOException { private void submitData(HttpURLConnection connection) throws IOException {
connection.setDoOutput(true); connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("Content-Type", "application/json");
GitlabAPI.MAPPER.writeValue(connection.getOutputStream(), data); GitlabAPI.MAPPER.writeValue(connection.getOutputStream(), data);
} }
private boolean hasAttachments() {
return !attachments.isEmpty();
}
private boolean hasOutput() { private boolean hasOutput() {
return method.equals("POST") || method.equals("PUT") && !data.isEmpty(); return method.equals("POST") || method.equals("PUT") && !data.isEmpty();
} }
......
package org.gitlab.api.models;
public class GitlabUpload {
public final static String URL = "/uploads";
private String alt;
private String url;
private String markdown;
public String getAlt() {
return alt;
}
public void setAlt(String alt) {
this.alt = alt;
}
public String getMarkdown() {
return markdown;
}
public void setMarkdown(String markdown) {
this.markdown = markdown;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
package org.gitlab.api;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.gitlab.api.models.GitlabProject;
import org.gitlab.api.models.GitlabUpload;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@Ignore
public class GitlabUploadTest {
private static final String TEST_URL = System.getProperty("TEST_URL", "http://localhost");
private static final String TEST_TOKEN = System.getProperty("TEST_TOKEN", "y0E5b9761b7y4qk");
private static final String TEST_PROJECT = System.getProperty("TEST_PROJECT", "user/project");
@Test
public void testUploadToProject() throws Exception {
GitlabAPI api = GitlabAPI.connect(TEST_URL, TEST_TOKEN);
String content = "test file content";
File tempFile = createTempFile(content);
try {
GitlabUpload upload = api.uploadFile(gitlabProject(api), tempFile);
Assert.assertNotNull(upload.getUrl());
} finally {
tempFile.delete();
}
}
private File createTempFile(String content) throws IOException {
File tempFile = File.createTempFile("upload-", ".txt");
InputStream is = new ByteArrayInputStream(content.getBytes());
OutputStream os = new FileOutputStream(tempFile);
try {
IOUtils.copy(is, os);
} finally {
is.close();
os.close();
}
return tempFile;
}
private GitlabProject gitlabProject(GitlabAPI api) throws IOException {
for (GitlabProject gitlabProject : api.getProjects()) {
String projetPath = String.format("%s/%s", gitlabProject.getNamespace().getPath(), gitlabProject.getPath());
if (projetPath.equals(TEST_PROJECT)) {
return gitlabProject;
}
}
throw new IllegalStateException();
}
}
Markdown is supported
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