Skip to content

implement Attachments API #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@
<properties>
<!-- dependencies -->
<jdk.version>1.8</jdk.version>
<lombok.version>1.16.2</lombok.version>
<jackson.version>2.3.1</jackson.version>
<lombok.version>1.18.0</lombok.version>
<jackson.version>2.10.3</jackson.version>
<guava.version>21.0</guava.version>
<log4j.version>1.2.17</log4j.version>
<junit.version>4.11</junit.version>
<mockito.version>1.9.5</mockito.version>

<!-- plugins -->
<lombok.plugin.version>1.16.2.0</lombok.plugin.version>
<maven.compiler.plugin.version>3.1</maven.compiler.plugin.version>
<lombok.plugin.version>1.18.0.0</lombok.plugin.version>
<maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
<maven.source.plugin.version>2.4</maven.source.plugin.version>
<jacoco.plugin.version>0.7.3.201502191951</jacoco.plugin.version>
<maven.javadoc.plugin.version>3.0.0-M1</maven.javadoc.plugin.version>
Expand Down
58 changes: 47 additions & 11 deletions src/main/java/com/codepine/api/testrail/Request.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;

import javax.xml.bind.DatatypeConverter;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
Expand All @@ -54,7 +53,10 @@
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.util.Base64;

import static com.codepine.api.testrail.Request.Method.POST;
import static com.codepine.api.testrail.internal.FileHelper.createFileFromIStream;

/**
* TestRail request.
Expand Down Expand Up @@ -117,17 +119,15 @@ public T execute() {
if (config.getApplicationName().isPresent()) {
con.setRequestProperty("User-Agent", config.getApplicationName().get());
}
con.setRequestProperty("Content-Type", "application/json");
String basicAuth = "Basic "
+ DatatypeConverter.printBase64Binary((config.getUsername()
+ ":" + config.getPassword()).getBytes(Charset.forName("UTF-8")));
con.setRequestProperty("Authorization", basicAuth);
if (method == Method.POST) {
con.addRequestProperty("Content-Type", getContentType());
String auth = getAuthorization(config.getUsername(), config.getPassword());
con.addRequestProperty("Authorization", "Basic " + auth);
if (method == POST) {
con.setDoOutput(true);
Object content = getContent();
if (content != null) {
try (OutputStream outputStream = new BufferedOutputStream(con.getOutputStream())) {
JSON.writerWithView(this.getClass()).writeValue(outputStream, content);
try (OutputStream outputStream = con.getOutputStream()) {
write(content, outputStream);
}
} else {
con.setFixedLengthStreamingMode(0);
Expand Down Expand Up @@ -159,6 +159,11 @@ public T execute() {
if (responseClass == Void.class) {
return null;
}
if (responseClass == String.class) {
String filePath = (String) getContent();
createFileFromIStream(responseStream, filePath);
return (T) filePath;
}
if (supplementForDeserialization != null) {
return JSON.reader(responseClass).with(new InjectableValues.Std().addValue(responseClass.toString(), supplementForDeserialization)).readValue(responseStream);
}
Expand Down Expand Up @@ -203,6 +208,18 @@ private String getUrl() throws IOException {
return urlBuilder.toString();
}

/**
* Override this method to body to be send with {@code Method#POST} requests.
*
* @param content content to be send with {@code Method#POST}
* @param conOutputStream the HttpURLConnection output stream
*/
void write(final Object content, final OutputStream conOutputStream) throws IOException {
try (OutputStream outputStream = new BufferedOutputStream(conOutputStream)) {
JSON.writerWithView(this.getClass()).writeValue(outputStream, content);
}
}

/**
* Override this method to provide content to be send with {@code Method#POST} requests.
*
Expand Down Expand Up @@ -230,11 +247,30 @@ void setUrlConnectionFactory(UrlConnectionFactory urlConnectionFactory) {
this.urlConnectionFactory = urlConnectionFactory;
}

/**
* Override this method to change Content-Type to be send with {@code Method#POST} requests headers.
*
* @return content
*/
String getContentType() {
return "application/json";
}

/**
* Allowed HTTP methods.
*/
static enum Method {
enum Method {
GET, POST;
}

private static String getAuthorization(String user, String password) {
try {
return new String(Base64.getEncoder().encode((user + ":" + password).getBytes()));
} catch (IllegalArgumentException e) {
// Not thrown
}

return "";
}

}
179 changes: 179 additions & 0 deletions src/main/java/com/codepine/api/testrail/TestRail.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

import com.codepine.api.testrail.internal.BooleanToIntSerializer;
import com.codepine.api.testrail.internal.ListToCsvSerializer;
import com.codepine.api.testrail.model.Attachment;
import com.codepine.api.testrail.model.AttachmentId;
import com.codepine.api.testrail.model.Case;
import com.codepine.api.testrail.model.CaseField;
import com.codepine.api.testrail.model.CaseType;
Expand Down Expand Up @@ -53,9 +55,16 @@
import lombok.Setter;
import lombok.experimental.Accessors;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Date;

import static com.codepine.api.testrail.internal.FileHelper.writeFileInOStream;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.nonNull;

/**
* Client for Test Rail API. Configure and use it to create requests for the API.
Expand Down Expand Up @@ -226,6 +235,15 @@ public Results results() {
return new Results();
}

/**
* An accessor for creating attachments for "Attachments".
*
* @return a request factory
*/
public Attachments attachments() {
return new Attachments();
}

/**
* Builder for {@code TestRail}.
*/
Expand Down Expand Up @@ -2071,4 +2089,165 @@ private List() {
}

}

/**
* Request factories for "Attachments".
*/
@NoArgsConstructor
public class Attachments {

/**
* Adds an attachment inside result.
*
* @param resultId the ID of the result where to add attachment
* @param filePath the path of the file
* @return the request
* @throws java.lang.IllegalArgumentException if resultId is not positive or file path is empty or null
* @throws java.lang.NullPointerException if any other argument is null
*/
public Add add(final int resultId, String filePath) {
checkArgument(resultId > 0, "resultId should be positive");
checkArgument(!filePath.isEmpty() && nonNull(filePath), "specify filePath");
return new Attachments.Add(resultId, filePath);
}

/**
* Returns a list of attachments for a case id.
*
* @param caseId the ID of the case to get the attachments for
* @return the request
* @throws java.lang.IllegalArgumentException if caseId is not positive
*/
public Attachments.ListForCase listForCase(final int caseId) {
checkArgument(caseId > 0, "caseId should be positive");
return new Attachments.ListForCase(caseId);
}

/**
* Returns a list of attachments for a test id.
*
* @param testId the ID of the case to get the attachments for
* @return the request
* @throws java.lang.IllegalArgumentException if testId is not positive
*/
public Attachments.ListForTest listForTest(final int testId) {
checkArgument(testId > 0, "testId should be positive");
return new Attachments.ListForTest(testId);
}

/**
* Returns attachment path where it is put.
*
* @param attachmentId the ID of the attachment
* @param filePath the path of the file where attachment will be put
* @return the request
* @throws java.lang.IllegalArgumentException if attachmentId is not positive or file path is empty or null
*/
public Attachments.Get get(final int attachmentId, String filePath) {
checkArgument(attachmentId > 0, "attachmentId should be positive");
checkArgument(!filePath.isEmpty() && nonNull(filePath), "specify filePath");
return new Attachments.Get(attachmentId, filePath);
}

/**
* Deletes an existing attachment.
*
* @param attachmentId the ID of the attachment to be deleted
* @return the request
* @throws java.lang.IllegalArgumentException if attachmentId is not positive
*/
public Attachments.Delete delete(final int attachmentId) {
checkArgument(attachmentId > 0, "attachmentId should be positive");
return new Attachments.Delete(attachmentId);
}

public class Add extends Request<AttachmentId> {
private static final String REST_PATH = "add_attachment_to_result/";

private final String boundary = "TestRailAPIAttachmentBoundary";

private File uploadFile;

private Add(int resultId, String filePath) {
super(config, Method.POST, REST_PATH + resultId, AttachmentId.class);
this.uploadFile = new File(filePath);
}

@Override
void write(final Object content, final OutputStream oStream) throws IOException {
File file = (File) content;

try (BufferedWriter bodyWriter = new BufferedWriter(new OutputStreamWriter(oStream))) {
bodyWriter.write("\n\n--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"attachment\"; filename=\"" + file.getName()
+ "\"" + "\r\n\r\n");
bodyWriter.flush();

writeFileInOStream(oStream, file);
bodyWriter.flush();

bodyWriter.write("\r\n--" + boundary + "--\r\n");
bodyWriter.flush();
}
}

@Override
Object getContent() {
return uploadFile;
}

@Override
String getContentType() {
return "multipart/form-data; boundary=" + boundary;
}
}

@Getter
@Setter
public class ListForCase extends Request<java.util.List<Attachment>> {
private static final String REST_PATH = "get_attachments_for_case/";

private ListForCase(int caseId) {
super(config, Method.GET, REST_PATH + caseId, new TypeReference<java.util.List<Attachment>>() {
});
}
}

@Getter
@Setter
public class ListForTest extends Request<java.util.List<Attachment>> {
private static final String REST_PATH = "get_attachments_for_test/";

private ListForTest(int testId) {
super(config, Method.GET, REST_PATH + testId, new TypeReference<java.util.List<Attachment>>() {
});
}
}

public class Get extends Request<String> {
private static final String REST_PATH = "get_attachment/";

private String filePath;

private Get(int attachmentId, String filePath) {
super(config, Method.GET, REST_PATH + attachmentId, String.class);
this.filePath = filePath;
}

@Override
Object getContent() {
return filePath;
}
}

public class Delete extends Request<Void> {
private static final String REST_PATH = "delete_attachment/";

private Delete(int attachmentId) {
super(config, Method.POST, REST_PATH + attachmentId, Void.class);
}

}

}
}
31 changes: 31 additions & 0 deletions src/main/java/com/codepine/api/testrail/internal/FileHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.codepine.api.testrail.internal;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileHelper {

public static void writeFileInOStream(OutputStream outputStream, File file) throws IOException {
try (InputStream inputStream = new FileInputStream(file)) {
int bytesRead;
byte[] dataBuffer = new byte[1024];
while ((bytesRead = inputStream.read(dataBuffer)) != -1) {
outputStream.write(dataBuffer, 0, bytesRead);
}
}
}

public static void createFileFromIStream(InputStream inputStream, String filePath) throws IOException {
try (FileOutputStream outputStream = new FileOutputStream(filePath)) {
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
}
Loading