diff --git a/src/main/java/org/gitlab4j/api/CommitsApi.java b/src/main/java/org/gitlab4j/api/CommitsApi.java
index 5c0381190..7b1425483 100644
--- a/src/main/java/org/gitlab4j/api/CommitsApi.java
+++ b/src/main/java/org/gitlab4j/api/CommitsApi.java
@@ -13,6 +13,7 @@
import org.gitlab4j.api.models.Comment;
import org.gitlab4j.api.models.Commit;
import org.gitlab4j.api.models.CommitAction;
+import org.gitlab4j.api.models.CommitAction.Action;
import org.gitlab4j.api.models.CommitPayload;
import org.gitlab4j.api.models.CommitRef;
import org.gitlab4j.api.models.CommitRef.RefType;
@@ -23,6 +24,7 @@
/**
* This class implements the client side API for the GitLab commits calls.
+ * See Commits API at GitLab for more information.
*/
public class CommitsApi extends AbstractApi {
@@ -566,6 +568,23 @@ public Comment addComment(Object projectIdOrPath, String sha, String note) throw
public Commit createCommit(Object projectIdOrPath, String branch, String commitMessage, String startBranch,
String authorEmail, String authorName, List actions) throws GitLabApiException {
+ // Validate the actions
+ if (actions == null || actions.isEmpty()) {
+ throw new GitLabApiException("actions cannot be null or empty.");
+ }
+
+ for (CommitAction action : actions) {
+
+ // File content is required for create and update
+ Action actionType = action.getAction();
+ if (actionType == Action.CREATE || actionType == Action.UPDATE) {
+ String content = action.getContent();
+ if (content == null) {
+ throw new GitLabApiException("Content cannot be null for create or update actions.");
+ }
+ }
+ }
+
CommitPayload payload = new CommitPayload();
payload.setBranch(branch);
payload.setCommitMessage(commitMessage);
@@ -574,7 +593,8 @@ public Commit createCommit(Object projectIdOrPath, String branch, String commitM
payload.setAuthorName(authorName);
payload.setActions(actions);
- Response response = post(Response.Status.CREATED, payload, "projects", getProjectIdOrPath(projectIdOrPath), "repository", "commits");
+ Response response = post(Response.Status.CREATED, payload,
+ "projects", getProjectIdOrPath(projectIdOrPath), "repository", "commits");
return (response.readEntity(Commit.class));
}
}
diff --git a/src/main/java/org/gitlab4j/api/Constants.java b/src/main/java/org/gitlab4j/api/Constants.java
index c8bbfa9c6..47f04797e 100644
--- a/src/main/java/org/gitlab4j/api/Constants.java
+++ b/src/main/java/org/gitlab4j/api/Constants.java
@@ -39,6 +39,28 @@ public enum TokenType {
ACCESS, OAUTH2_ACCESS, PRIVATE;
}
+ /** Enum to specify encoding of file contents. */
+ public enum Encoding {
+ TEXT, BASE64;
+
+ private static JacksonJsonEnumHelper enumHelper = new JacksonJsonEnumHelper<>(Encoding.class);
+
+ @JsonCreator
+ public static Encoding forValue(String value) {
+ return enumHelper.forValue((value != null ? value.toLowerCase() : value));
+ }
+
+ @JsonValue
+ public String toValue() {
+ return (enumHelper.toString(this));
+ }
+
+ @Override
+ public String toString() {
+ return (enumHelper.toString(this));
+ }
+ }
+
/** Enum to use for ordering the results of various API calls. */
public enum SortOrder {
diff --git a/src/main/java/org/gitlab4j/api/RepositoryFileApi.java b/src/main/java/org/gitlab4j/api/RepositoryFileApi.java
index c64ed5325..32013e32c 100644
--- a/src/main/java/org/gitlab4j/api/RepositoryFileApi.java
+++ b/src/main/java/org/gitlab4j/api/RepositoryFileApi.java
@@ -16,6 +16,7 @@
/**
* This class provides an entry point to all the GitLab API repository files calls.
+ * See Repository Files API at GitLab for more information.
*/
public class RepositoryFileApi extends AbstractApi {
@@ -67,7 +68,10 @@ public RepositoryFile getFileInfo(Object projectIdOrPath, String filePath, Strin
RepositoryFile file = new RepositoryFile();
file.setBlobId(response.getHeaderString("X-Gitlab-Blob-Id"));
file.setCommitId(response.getHeaderString("X-Gitlab-Commit-Id"));
- file.setEncoding(response.getHeaderString("X-Gitlab-Encoding"));
+
+ String encoding = response.getHeaderString("X-Gitlab-Encoding");
+ file.setEncoding(Constants.Encoding.forValue(encoding));
+
file.setFileName(response.getHeaderString("X-Gitlab-File-Name"));
file.setFilePath(response.getHeaderString("X-Gitlab-File-Path"));
file.setLastCommitId(response.getHeaderString("X-Gitlab-Last-Commit-Id"));
diff --git a/src/main/java/org/gitlab4j/api/models/CommitAction.java b/src/main/java/org/gitlab4j/api/models/CommitAction.java
index 02036231c..0b1335023 100644
--- a/src/main/java/org/gitlab4j/api/models/CommitAction.java
+++ b/src/main/java/org/gitlab4j/api/models/CommitAction.java
@@ -1,5 +1,11 @@
package org.gitlab4j.api.models;
+import java.io.File;
+import java.io.IOException;
+
+import org.gitlab4j.api.Constants.Encoding;
+import org.gitlab4j.api.GitLabApiException;
+import org.gitlab4j.api.utils.FileUtils;
import org.gitlab4j.api.utils.JacksonJson;
import org.gitlab4j.api.utils.JacksonJsonEnumHelper;
@@ -30,28 +36,6 @@ public String toString() {
}
}
- public enum Encoding {
-
- BASE64, TEXT;
-
- private static JacksonJsonEnumHelper enumHelper = new JacksonJsonEnumHelper<>(Encoding.class);
-
- @JsonCreator
- public static Encoding forValue(String value) {
- return enumHelper.forValue(value);
- }
-
- @JsonValue
- public String toValue() {
- return (enumHelper.toString(this));
- }
-
- @Override
- public String toString() {
- return (enumHelper.toString(this));
- }
- }
-
private Action action;
private String filePath;
private String previousPath;
@@ -151,6 +135,25 @@ public CommitAction withExecuteFilemode(Boolean executeFilemode) {
return this;
}
+ public CommitAction withFileContent(String filePath, Encoding encoding) throws GitLabApiException {
+ File file = new File(filePath);
+ return (withFileContent(file, filePath, encoding));
+ }
+
+ public CommitAction withFileContent(File file, String filePath, Encoding encoding) throws GitLabApiException {
+
+ this.encoding = (encoding != null ? encoding : Encoding.TEXT);
+ this.filePath = filePath;
+
+ try {
+ content = FileUtils.getFileContentAsString(file, this.encoding);
+ } catch (IOException e) {
+ throw new GitLabApiException(e);
+ }
+
+ return (this);
+ }
+
@Override
public String toString() {
return (JacksonJson.toJsonString(this));
diff --git a/src/main/java/org/gitlab4j/api/models/RepositoryFile.java b/src/main/java/org/gitlab4j/api/models/RepositoryFile.java
index daafdbaa2..6496269a9 100644
--- a/src/main/java/org/gitlab4j/api/models/RepositoryFile.java
+++ b/src/main/java/org/gitlab4j/api/models/RepositoryFile.java
@@ -3,6 +3,7 @@
import java.util.Base64;
+import org.gitlab4j.api.Constants.Encoding;
import org.gitlab4j.api.utils.JacksonJson;
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -12,7 +13,7 @@ public class RepositoryFile {
private String fileName; // file name only, Ex. class.rb
private String filePath; // full path to file. Ex. lib/class.rb
private Integer size;
- private String encoding;
+ private Encoding encoding;
private String content;
private String ref;
private String blobId;
@@ -43,11 +44,11 @@ public void setSize(Integer size) {
this.size = size;
}
- public String getEncoding() {
+ public Encoding getEncoding() {
return encoding;
}
- public void setEncoding(String encoding) {
+ public void setEncoding(Encoding encoding) {
this.encoding = encoding;
}
@@ -104,7 +105,7 @@ public String getDecodedContentAsString() {
return (null);
}
- if ("base64".equalsIgnoreCase(encoding)) {
+ if (Encoding.BASE64.equals(encoding)) {
return (new String(Base64.getDecoder().decode(content)));
}
@@ -124,7 +125,7 @@ public byte[] getDecodedContentAsBytes() {
return (null);
}
- if ("base64".equalsIgnoreCase(encoding)) {
+ if (encoding == Encoding.BASE64) {
return (Base64.getDecoder().decode(content));
}
@@ -157,7 +158,7 @@ public void encodeAndSetContent(byte[] byteContent) {
}
this.content = Base64.getEncoder().encodeToString(byteContent);
- encoding = "base64";
+ encoding = Encoding.BASE64;
}
@Override
diff --git a/src/main/java/org/gitlab4j/api/utils/FileUtils.java b/src/main/java/org/gitlab4j/api/utils/FileUtils.java
index f3f7aad18..1316f72c4 100644
--- a/src/main/java/org/gitlab4j/api/utils/FileUtils.java
+++ b/src/main/java/org/gitlab4j/api/utils/FileUtils.java
@@ -1,12 +1,17 @@
package org.gitlab4j.api.utils;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.Reader;
+import java.nio.file.Files;
+import java.util.Base64;
import java.util.Scanner;
import javax.ws.rs.core.Response;
+import org.gitlab4j.api.Constants.Encoding;
+
/**
* This class provides static utility methods used throughout GitLab4J.
*/
@@ -79,7 +84,7 @@ public static String readFileContents(File file) throws IOException {
/**
* Reads the content of a Reader instance and returns it as a String.
- *
+ *
* @param reader the Reader instance to read the content from
* @return the content of a Reader instance as a String
* @throws IOException if any error occurs
@@ -95,4 +100,27 @@ public static String getReaderContentAsString(Reader reader) throws IOException
return (out.toString());
}
+
+ /**
+ * Reads the content of a File instance and returns it as a String of either text or base64 encoded text.
+ *
+ * @param file the File instance to read from
+ * @param encoding whether to encode as Base64 or as Text, defaults to Text if null
+ * @return the content of the File as a String
+ * @throws IOException if any error occurs
+ */
+ public static String getFileContentAsString(File file, Encoding encoding) throws IOException {
+
+ if (encoding == Encoding.BASE64) {
+
+ try (FileInputStream stream = new FileInputStream(file)) {
+ byte data[] = new byte[(int) file.length()];
+ stream.read(data);
+ return (Base64.getEncoder().encodeToString(data));
+ }
+
+ } else {
+ return(new String (Files.readAllBytes(file.toPath())));
+ }
+ }
}
diff --git a/src/test/java/org/gitlab4j/api/TestCommitsApi.java b/src/test/java/org/gitlab4j/api/TestCommitsApi.java
index b6f9ef14d..b7c5fee31 100644
--- a/src/test/java/org/gitlab4j/api/TestCommitsApi.java
+++ b/src/test/java/org/gitlab4j/api/TestCommitsApi.java
@@ -1,24 +1,29 @@
package org.gitlab4j.api;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import java.text.ParseException;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
+import java.util.Optional;
import javax.ws.rs.core.Response;
import org.gitlab4j.api.models.Comment;
import org.gitlab4j.api.models.Commit;
+import org.gitlab4j.api.models.CommitAction;
+import org.gitlab4j.api.models.CommitAction.Action;
import org.gitlab4j.api.models.CommitRef;
import org.gitlab4j.api.models.Diff;
import org.gitlab4j.api.models.Project;
import org.gitlab4j.api.models.RepositoryFile;
import org.gitlab4j.api.utils.ISO8601;
-import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -40,9 +45,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestCommitsApi extends AbstractIntegrationTest {
+ private static final String TEST_CREATE_COMMIT_FILEPATH = "gitlab4j-create-commit-test-file.txt";
private static GitLabApi gitLabApi;
private static Project testProject;
- private static boolean createdSubDirectoryPathFile = false;
public TestCommitsApi() {
super();
@@ -60,20 +65,10 @@ public static void setup() {
repoFile.setFilePath(TEST_PROJECT_SUBDIRECTORY_PATH);
repoFile.setContent("This is a test project used to test GitLab4J-API.");
gitLabApi.getRepositoryFileApi().createFile(testProject, repoFile, "master", "Initial commit.");
- createdSubDirectoryPathFile = true;
} catch (GitLabApiException ignore) {}
}
}
- @AfterClass
- public static void teardown() {
- if (createdSubDirectoryPathFile) {
- try {
- gitLabApi.getRepositoryFileApi().deleteFile(testProject, TEST_PROJECT_SUBDIRECTORY_PATH, "master", "No longer needed.");
- } catch (Exception ignore) {}
- }
- }
-
@Before
public void beforeMethod() {
assumeTrue(gitLabApi != null);
@@ -198,4 +193,98 @@ public void testCommitsByPathNotFound() throws GitLabApiException {
assertEquals(Response.Status.NOT_FOUND, gle.getHttpStatus());
}
}
+
+ @Test
+ public void testCreateCommit() throws GitLabApiException {
+
+ // Make sure the file to create does not exist.
+ if (gitLabApi.getRepositoryFileApi().getOptionalFile(testProject, TEST_CREATE_COMMIT_FILEPATH, "master").isPresent()) {
+ gitLabApi.getRepositoryFileApi().deleteFile(testProject, TEST_CREATE_COMMIT_FILEPATH, "master", "Deleted test file");
+ }
+
+ // Arrange
+ CommitAction commitAction = new CommitAction()
+ .withAction(Action.CREATE)
+ .withContent("This is the original data in the file")
+ .withFilePath(TEST_CREATE_COMMIT_FILEPATH);
+
+ // Act
+ Commit commit = gitLabApi.getCommitsApi().createCommit(
+ testProject, "master", "Testing createCommit() create action", null, null, null, Arrays.asList(commitAction));
+
+ // Assert
+ assertNotNull(commit);
+
+ // Arrange
+ commitAction = new CommitAction()
+ .withAction(Action.DELETE)
+ .withFilePath(TEST_CREATE_COMMIT_FILEPATH);
+
+ // Act
+ commit = gitLabApi.getCommitsApi().createCommit(
+ testProject, "master", "Testing createCommit() delete action", null, null, null, Arrays.asList(commitAction));
+
+ // Assert
+ assertNotNull(commit);
+
+ Optional repoFile = gitLabApi.getRepositoryFileApi().getOptionalFile(testProject, TEST_CREATE_COMMIT_FILEPATH, "master");
+ assertFalse(repoFile.isPresent());
+ }
+
+ @Test
+ public void testCreateCommitFromFile() throws GitLabApiException {
+
+ // Make sure the file to create does not exist.
+ if (gitLabApi.getRepositoryFileApi().getOptionalFile(testProject, TEST_CREATE_COMMIT_FILEPATH, "master").isPresent()) {
+ try {
+ gitLabApi.getRepositoryFileApi().deleteFile(testProject, TEST_CREATE_COMMIT_FILEPATH, "master", "Deleted test file");
+ } catch (GitLabApiException ignore) {}
+ }
+
+ // Arrange
+ CommitAction commitAction = new CommitAction()
+ .withAction(Action.CREATE)
+ .withContent("This is the original data in the file")
+ .withFilePath(TEST_CREATE_COMMIT_FILEPATH);
+
+ // Act
+ Commit commit = gitLabApi.getCommitsApi().createCommit(
+ testProject, "master", "Testing createCommit() create action", null, null, null, Arrays.asList(commitAction));
+
+ // Assert
+ assertNotNull(commit);
+
+ // Arrange
+ commitAction = new CommitAction()
+ .withAction(Action.DELETE)
+ .withFilePath(TEST_CREATE_COMMIT_FILEPATH);
+
+ // Act
+ commit = gitLabApi.getCommitsApi().createCommit(
+ testProject, "master", "Testing createCommit() delete action", null, null, null, Arrays.asList(commitAction));
+
+ // Assert
+ assertNotNull(commit);
+
+ Optional repoFile = gitLabApi.getRepositoryFileApi().getOptionalFile(testProject, TEST_CREATE_COMMIT_FILEPATH, "master");
+ assertFalse(repoFile.isPresent());
+ }
+
+ @Test
+ public void testCreateCommitCreateWithNoContent() throws GitLabApiException {
+
+ // Arrange
+ CommitAction commitAction = new CommitAction()
+ .withAction(Action.CREATE)
+ .withContent(null)
+ .withFilePath(TEST_CREATE_COMMIT_FILEPATH + ".bk");
+
+ // Act - expecting exception
+ try {
+ gitLabApi.getCommitsApi().createCommit(testProject, "master", "Testing createCommit() create action",
+ null, null, null, Arrays.asList(commitAction));
+ fail("Commit should have been rejected due to no content.");
+ } catch (GitLabApiException ignore) {
+ }
+ }
}
\ No newline at end of file