diff --git a/src/main/java/org/gitlab4j/api/HookManager.java b/src/main/java/org/gitlab4j/api/HookManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e16df2059c0cfa25ab2f45eb8fe15b3e6dde20b
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/HookManager.java
@@ -0,0 +1,77 @@
+
+package org.gitlab4j.api;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * This class provides a base class handler for processing GitLab Web Hook and System Hook callouts.
+ */
+public abstract class HookManager {
+
+ private String secretToken;
+
+ /**
+ * Create a HookManager to handle GitLab hook events.
+ */
+ public HookManager() {
+ this.secretToken = null;
+ }
+
+ /**
+ * Create a HookManager to handle GitLab hook events which will be verified
+ * against the specified secretToken.
+ *
+ * @param secretToken the secret token to verify against
+ */
+ public HookManager(String secretToken) {
+ this.secretToken = secretToken;
+ }
+
+ /**
+ * Set the secret token that received hook events should be validated against.
+ *
+ * @param secretToken the secret token to verify against
+ */
+ public void setSecretToken(String secretToken) {
+ this.secretToken = secretToken;
+ }
+
+ /**
+ * Validate the provided secret token against the reference secret token. Returns true if
+ * the secret token is valid or there is no reference secret token to validate against,
+ * otherwise returns false.
+ *
+ * @param secretToken the token to validate
+ * @return true if the secret token is valid or there is no reference secret token to validate against
+ */
+ public boolean isValidSecretToken(String secretToken) {
+ return (this.secretToken == null || this.secretToken.equals(secretToken) ? true : false);
+ }
+
+ /**
+ * Validate the provided secret token found in the HTTP header against the reference secret token.
+ * Returns true if the secret token is valid or there is no reference secret token to validate
+ * against, otherwise returns false.
+ *
+ * @param request the HTTP request to verify the secret token
+ * @return true if the secret token is valid or there is no reference secret token to validate against
+ */
+ public boolean isValidSecretToken(HttpServletRequest request) {
+
+ if (this.secretToken != null) {
+ String secretToken = request.getHeader("X-Gitlab-Token");
+ return (isValidSecretToken(secretToken));
+ }
+
+ return (true);
+ }
+
+ /**
+ * Parses and verifies an Event instance from the HTTP request and
+ * fires it off to the registered listeners.
+ *
+ * @param request the HttpServletRequest to read the Event instance from
+ * @throws GitLabApiException if the parsed event is not supported
+ */
+ public abstract void handleEvent(HttpServletRequest request) throws GitLabApiException;
+}
\ No newline at end of file
diff --git a/src/main/java/org/gitlab4j/api/SystemHooksApi.java b/src/main/java/org/gitlab4j/api/SystemHooksApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..b665e66e4e72dd44831854a1ab206abf92d931f2
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/SystemHooksApi.java
@@ -0,0 +1,160 @@
+package org.gitlab4j.api;
+
+import java.util.List;
+
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+
+import org.gitlab4j.api.GitLabApi.ApiVersion;
+import org.gitlab4j.api.models.SystemHook;
+
+/**
+ * This class implements the client side API for the GitLab System Hooks Keys API calls.
+ */
+public class SystemHooksApi extends AbstractApi {
+
+ public SystemHooksApi(GitLabApi gitLabApi) {
+ super(gitLabApi);
+ }
+
+ /**
+ * Get a list of all system hooks. This method requires admin access.
+ * Only returns the first page. This method requires admin access.
+ *
+ * GET /hooks
+ *
+ * @return a list of SystemHookEvent
+ * @throws GitLabApiException if any exception occurs
+ */
+ public List getSystemHooks() throws GitLabApiException {
+ return (getSystemHooks(1, getDefaultPerPage()));
+ }
+
+ /**
+ * Get a list of all system hooks using the specified page and per page settings.
+ * This method requires admin access.
+ *
+ * GET /hooks
+ *
+ * @param page the page to get
+ * @param perPage the number of deploy keys per page
+ * @return the list of SystemHookEvent in the specified range
+ * @throws GitLabApiException if any exception occurs
+ */
+ public List getSystemHooks(int page, int perPage) throws GitLabApiException {
+ Response response = get(Response.Status.OK, getPageQueryParams(page, perPage), "hooks");
+ return (response.readEntity(new GenericType>() {}));
+ }
+
+ /**
+ * Get a Pager of all system hooks. This method requires admin access.
+ *
+ * GET /hooks
+ *
+ * @param itemsPerPage the number of SystemHookEvent instances that will be fetched per page
+ * @return a Pager of SystemHookEvent
+ * @throws GitLabApiException if any exception occurs
+ */
+ public Pager getSystemHooks(int itemsPerPage) throws GitLabApiException {
+ return (new Pager(this, SystemHook.class, itemsPerPage, null, "hooks"));
+ }
+
+ /**
+ * Add a new system hook. This method requires admin access.
+ *
+ * POST /hooks
+ *
+ * @param url the hook URL, required
+ * @param token secret token to validate received payloads, optional
+ * @param pushEvents when true, the hook will fire on push events, optional
+ * @param tagPushEvents when true, the hook will fire on new tags being pushed, optional
+ * @param enablSsslVerification do SSL verification when triggering the hook, optional
+ * @return an SystemHookEvent instance with info on the added system hook
+ * @throws GitLabApiException if any exception occurs
+ */
+ public SystemHook addSystemHook(String url, String token, Boolean pushEvents,
+ Boolean tagPushEvents, Boolean enablSsslVerification) throws GitLabApiException {
+
+ if (url == null) {
+ throw new RuntimeException("url cannot be null");
+ }
+
+ GitLabApiForm formData = new GitLabApiForm()
+ .withParam("url", url, true)
+ .withParam("token", token)
+ .withParam("push_events", pushEvents)
+ .withParam("tag_push_events", tagPushEvents)
+ .withParam("enable_ssl_verification", enablSsslVerification);
+ Response response = post(Response.Status.CREATED, formData, "hooks");
+ return (response.readEntity(SystemHook.class));
+ }
+
+ /**
+ * Deletes a system hook. This method requires admin access.
+ *
+ * DELETE /hooks/:hook_id
+ *
+ * @param hook the SystemHook instance to delete
+ * @throws GitLabApiException if any exception occurs
+ */
+ public void deleteSystemHook(SystemHook hook) throws GitLabApiException {
+
+ if (hook == null) {
+ throw new RuntimeException("hook cannot be null");
+ }
+
+ deleteSystemHook(hook.getId());
+ }
+
+ /**
+ * Deletes a system hook. This method requires admin access.
+ *
+ * DELETE /hooks/:hook_id
+ *
+ * @param hookId the ID of the system hook to delete
+ * @throws GitLabApiException if any exception occurs
+ */
+ public void deleteSystemHook(Integer hookId) throws GitLabApiException {
+
+ if (hookId == null) {
+ throw new RuntimeException("hookId cannot be null");
+ }
+
+ Response.Status expectedStatus = (isApiVersion(ApiVersion.V3) ? Response.Status.OK : Response.Status.NO_CONTENT);
+ delete(expectedStatus, null, "hooks", hookId);
+ }
+
+ /**
+ * Test a system hook. This method requires admin access.
+ *
+ * GET /hooks/:hook_id
+ *
+ * @param hook the SystemHookEvent instance to test
+ * @throws GitLabApiException if any exception occurs
+ */
+ public void testSystemHook(SystemHook hook) throws GitLabApiException {
+
+ if (hook == null) {
+ throw new RuntimeException("hook cannot be null");
+ }
+
+ testSystemHook(hook.getId());
+ }
+
+ /**
+ * Test a system hook. This method requires admin access.
+ *
+ * GET /hooks/:hook_id
+ *
+ * @param hookId the ID of the system hook to test
+ * @throws GitLabApiException if any exception occurs
+ */
+ public void testSystemHook(Integer hookId) throws GitLabApiException {
+
+ if (hookId == null) {
+ throw new RuntimeException("hookId cannot be null");
+ }
+
+ get(Response.Status.OK, null, "hooks", hookId);
+ }
+}
diff --git a/src/main/java/org/gitlab4j/api/systemhooks/ProjectSystemHookEvent.java b/src/main/java/org/gitlab4j/api/systemhooks/ProjectSystemHookEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..39ba57f2348aff65ac3eedf0ffcc957a40d4d527
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/systemhooks/ProjectSystemHookEvent.java
@@ -0,0 +1,120 @@
+package org.gitlab4j.api.systemhooks;
+
+import java.util.Date;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.gitlab4j.api.models.Visibility;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ProjectSystemHookEvent implements SystemHookEvent {
+
+ public static final String PROJECT_CREATE_EVENT = "project_create";
+ public static final String PROJECT_DESTROY_EVENT = "project_destroy";
+ public static final String PROJECT_RENAME_EVENT = "project_rename";
+ public static final String PROJECT_TRANSFER_EVENT = "project_transfer";
+ public static final String PROJECT_UPDATE_EVENT = "project_update";
+
+ private Date createdAt;
+ private Date updatedAt;
+ private String eventName;
+ private String name;
+ private String ownerEmail;
+ private String ownerName;
+ private String path;
+ private Integer projectId;
+ private String pathWithNamespace;
+ private Visibility projectVisibility;
+ private String oldPathWithNamespace;
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Date getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Date updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ public String getEventName() {
+ return this.eventName;
+ }
+
+ public void setEventName(String eventName) {
+ this.eventName = eventName;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getOwnerEmail() {
+ return this.ownerEmail;
+ }
+
+ public void setOwnerEmail(String ownerEmail) {
+ this.ownerEmail = ownerEmail;
+ }
+
+ public String getOwnerName() {
+ return this.ownerName;
+ }
+
+ public void setOwnerName(String ownerName) {
+ this.ownerName = ownerName;
+ }
+
+ public String getPath() {
+ return this.path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public Integer getProjectId() {
+ return this.projectId;
+ }
+
+ public void setProjectId(Integer projectId) {
+ this.projectId = projectId;
+ }
+
+ public String getPathWithNamespace() {
+ return pathWithNamespace;
+ }
+
+ public void setPathWithNamespace(String pathWithNamespace) {
+ this.pathWithNamespace = pathWithNamespace;
+ }
+
+ public Visibility getProjectVisibility() {
+ return projectVisibility;
+ }
+
+ public void setProjectVisibility(Visibility projectVisibility) {
+ this.projectVisibility = projectVisibility;
+ }
+
+ public String getOldPathWithNamespace() {
+ return oldPathWithNamespace;
+ }
+
+ public void setOldPathWithNamespace(String oldPathWithNamespace) {
+ this.oldPathWithNamespace = oldPathWithNamespace;
+ }
+}
diff --git a/src/main/java/org/gitlab4j/api/systemhooks/SystemHookEvent.java b/src/main/java/org/gitlab4j/api/systemhooks/SystemHookEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..c17454d88deee49f02fa7ab574e652a65bfb67fa
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/systemhooks/SystemHookEvent.java
@@ -0,0 +1,20 @@
+package org.gitlab4j.api.systemhooks;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,
+ include=JsonTypeInfo.As.PROPERTY,
+ property="event_name")
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = ProjectSystemHookEvent.class, name = ProjectSystemHookEvent.PROJECT_CREATE_EVENT),
+ @JsonSubTypes.Type(value = ProjectSystemHookEvent.class, name = ProjectSystemHookEvent.PROJECT_DESTROY_EVENT),
+ @JsonSubTypes.Type(value = ProjectSystemHookEvent.class, name = ProjectSystemHookEvent.PROJECT_RENAME_EVENT),
+ @JsonSubTypes.Type(value = ProjectSystemHookEvent.class, name = ProjectSystemHookEvent.PROJECT_TRANSFER_EVENT),
+ @JsonSubTypes.Type(value = ProjectSystemHookEvent.class, name = ProjectSystemHookEvent.PROJECT_UPDATE_EVENT),
+ @JsonSubTypes.Type(value = TeamMemberSystemHookEvent.class, name = TeamMemberSystemHookEvent.NEW_TEAM_MEMBER_EVENT),
+ @JsonSubTypes.Type(value = TeamMemberSystemHookEvent.class, name = TeamMemberSystemHookEvent.TEAM_MEMBER_REMOVED_EVENT)
+})
+public interface SystemHookEvent {
+ public String getEventName();
+}
diff --git a/src/main/java/org/gitlab4j/api/systemhooks/SystemHookListener.java b/src/main/java/org/gitlab4j/api/systemhooks/SystemHookListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c9a0181cf2580a475a5250b4fc0f1639d0fb204
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/systemhooks/SystemHookListener.java
@@ -0,0 +1,23 @@
+
+package org.gitlab4j.api.systemhooks;
+
+/**
+ * This class defines an event listener for the event fired when
+ * a System Hook notification has been received from a GitLab server.
+ */
+public interface SystemHookListener extends java.util.EventListener {
+
+ /**
+ * This method is called when a System Hook prject event has been received.
+ *
+ * @param event the ProjectSystemHookEvent instance
+ */
+ public void onProjectEvent(ProjectSystemHookEvent event);
+
+ /**
+ * This method is called when a System Hook team member event has been received.
+ *
+ * @param event the TeamMemberSystemHookEvent instance containing info on the team member event
+ */
+ public void onTeamMemberEvent(TeamMemberSystemHookEvent event);
+}
diff --git a/src/main/java/org/gitlab4j/api/systemhooks/SystemHookManager.java b/src/main/java/org/gitlab4j/api/systemhooks/SystemHookManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..fde49c1081687e0f4ed8f66631f3254243ad6931
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/systemhooks/SystemHookManager.java
@@ -0,0 +1,174 @@
+
+package org.gitlab4j.api.systemhooks;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.gitlab4j.api.GitLabApiException;
+import org.gitlab4j.api.HookManager;
+import org.gitlab4j.api.utils.HttpRequestUtils;
+import org.gitlab4j.api.utils.JacksonJson;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * This class provides a handler for processing GitLab System Hook callouts.
+ */
+public class SystemHookManager extends HookManager {
+
+ public static final String SYSTEM_HOOK_EVENT = "System Hook";
+
+ private final static Logger LOG = Logger.getLogger(SystemHookManager.class.getName());
+ private final JacksonJson jacksonJson = new JacksonJson();
+
+ // Collection of objects listening for System Hook events.
+ private final List systemHookListeners = new CopyOnWriteArrayList();
+
+ /**
+ * Create a HookManager to handle GitLab system hook events.
+ */
+ public SystemHookManager() {
+ super();
+ }
+
+ /**
+ * Create a HookManager to handle GitLab system hook events which will be verified
+ * against the specified secretToken.
+ *
+ * @param secretToken the secret token to verify against
+ */
+ public SystemHookManager(String secretToken) {
+ super(secretToken);
+ }
+
+ /**
+ * Parses and verifies an SystemHookEvent instance from the HTTP request and
+ * fires it off to the registered listeners.
+ *
+ * @param request the HttpServletRequest to read the Event instance from
+ * @throws GitLabApiException if the parsed event is not supported
+ */
+ public void handleEvent(HttpServletRequest request) throws GitLabApiException {
+
+ if (!isValidSecretToken(request)) {
+ String message = "X-Gitlab-Token mismatch!";
+ LOG.warning(message);
+ throw new GitLabApiException(message);
+ }
+
+ String eventName = request.getHeader("X-Gitlab-Event");
+ LOG.info("handleEvent: X-Gitlab-Event=" + eventName);
+ if (!SYSTEM_HOOK_EVENT.equals(eventName)) {
+ String message = "Unsupported X-Gitlab-Event, event Name=" + eventName;
+ LOG.warning(message);
+ throw new GitLabApiException(message);
+ }
+
+ String errorMessage = null;
+ try {
+
+ SystemHookEvent event;
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.fine(HttpRequestUtils.getShortRequestDump("System Hook", true, request));
+ String postData = HttpRequestUtils.getPostDataAsString(request);
+ LOG.fine("Raw POST data:\n" + postData);
+ event = jacksonJson.unmarshal(SystemHookEvent.class, postData);
+ LOG.fine(event.getEventName() + "\n" + jacksonJson.marshal(event) + "\n");
+ } else {
+ InputStreamReader reader = new InputStreamReader(request.getInputStream());
+ event = jacksonJson.unmarshal(SystemHookEvent.class, reader);
+ }
+
+ fireEvent(event);
+
+ } catch (JsonParseException jpe) {
+ errorMessage = jpe.getMessage();
+ LOG.warning("Error parsing JSON data, error=" + errorMessage);
+ } catch (JsonMappingException jme) {
+ errorMessage = jme.getMessage();
+ LOG.warning("Error mapping JSON data, error=" + errorMessage);
+ } catch (IOException ioe) {
+ errorMessage = ioe.getMessage();
+ LOG.warning("Error reading JSON data, error=" + errorMessage);
+ } catch (Exception e) {
+ errorMessage = e.getMessage();
+ LOG.warning("Unexpected error reading JSON data, error=" + errorMessage);
+ }
+
+ if (errorMessage != null)
+ throw new GitLabApiException(errorMessage);
+ }
+
+ /**
+ * Verifies the provided Event and fires it off to the registered listeners.
+ *
+ * @param event the Event instance to handle
+ * @throws GitLabApiException if the event is not supported
+ */
+ public void handleEvent(SystemHookEvent event) throws GitLabApiException {
+
+ LOG.info("handleEvent: object_kind=" + event.getEventName());
+ fireEvent(event);
+ }
+
+ /**
+ * Adds a System Hook event listener.
+ *
+ * @param listener the SystemHookListener to add
+ */
+ public void addListener(SystemHookListener listener) {
+
+ if (!systemHookListeners.contains(listener)) {
+ systemHookListeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes a System Hook event listener.
+ *
+ * @param listener the SystemHookListener to remove
+ */
+ public void removeListener(SystemHookListener listener) {
+ systemHookListeners.remove(listener);
+ }
+
+ /**
+ * Fire the event to the registered listeners.
+ *
+ * @param event the SystemHookEvent instance to fire to the registered event listeners
+ * @throws GitLabApiException if the event is not supported
+ */
+ public void fireEvent(SystemHookEvent event) throws GitLabApiException {
+
+ if (event instanceof ProjectSystemHookEvent) {
+ fireProjectEvent((ProjectSystemHookEvent) event);
+ } else if (event instanceof TeamMemberSystemHookEvent) {
+ fireTeamMemberEvent((TeamMemberSystemHookEvent) event);
+ } else {
+ String message = "Unsupported event, event_named=" + event.getEventName();
+ LOG.warning(message);
+ throw new GitLabApiException(message);
+ }
+ }
+
+ protected void fireProjectEvent(ProjectSystemHookEvent event) {
+
+ for (SystemHookListener listener : systemHookListeners) {
+ listener.onProjectEvent(event);
+ }
+ }
+
+ protected void fireTeamMemberEvent(TeamMemberSystemHookEvent event) {
+
+ for (SystemHookListener listener : systemHookListeners) {
+ listener.onTeamMemberEvent(event);
+ }
+ }
+}
diff --git a/src/main/java/org/gitlab4j/api/systemhooks/TeamMemberSystemHookEvent.java b/src/main/java/org/gitlab4j/api/systemhooks/TeamMemberSystemHookEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9602c33b705498524b94ef46e90a0836511ba9e
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/systemhooks/TeamMemberSystemHookEvent.java
@@ -0,0 +1,143 @@
+package org.gitlab4j.api.systemhooks;
+
+import java.util.Date;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.gitlab4j.api.models.Visibility;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class TeamMemberSystemHookEvent implements SystemHookEvent {
+
+ public static final String NEW_TEAM_MEMBER_EVENT = "user_add_to_team";
+ public static final String TEAM_MEMBER_REMOVED_EVENT = "user_remove_from_team";
+
+ private Date createdAt;
+ private Date updatedAt;
+ private String eventName;
+ private String projectAccess;
+ private String projectName;
+ private String projectPath;
+ private Integer projectId;
+ private String projectPathWithNamespace;
+ private String userEmail;
+ private String userName;
+ private String userUsername;
+ private Integer userId;
+ private Visibility projectVisibility;
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Date getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Date updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ public String getEventName() {
+ return this.eventName;
+ }
+
+ public void setEventName(String eventName) {
+ this.eventName = eventName;
+ }
+
+ public String getProjectAccess() {
+ return projectAccess;
+ }
+
+ public void setProjectAccess(String projectAccess) {
+ this.projectAccess = projectAccess;
+ }
+
+ public String getProjectName() {
+ return projectName;
+ }
+
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ public String getProjectPath() {
+ return projectPath;
+ }
+
+ public void setProjectPath(String projectPath) {
+ this.projectPath = projectPath;
+ }
+
+ public Integer getProjectId() {
+ return projectId;
+ }
+
+ public void setProjectId(Integer projectId) {
+ this.projectId = projectId;
+ }
+
+ public String getProjectPathWithNamespace() {
+ return projectPathWithNamespace;
+ }
+
+ public void setProjectPathWithNamespace(String projectPathWithNamespace) {
+ this.projectPathWithNamespace = projectPathWithNamespace;
+ }
+
+ public String getUserEmail() {
+ return userEmail;
+ }
+
+ public void setUserEmail(String userEmail) {
+ this.userEmail = userEmail;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getUserUsername() {
+ return userUsername;
+ }
+
+ public void setUserUsername(String userUsername) {
+ this.userUsername = userUsername;
+ }
+
+ public Integer getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Integer userId) {
+ this.userId = userId;
+ }
+
+ public Visibility getProjectVisibility() {
+ return projectVisibility;
+ }
+
+ public void setProjectVisibility(Visibility projectVisibility) {
+ this.projectVisibility = projectVisibility;
+ }
+
+ public static String getNewTeamMemberEvent() {
+ return NEW_TEAM_MEMBER_EVENT;
+ }
+
+ public static String getTeamMemberRemovedEvent() {
+ return TEAM_MEMBER_REMOVED_EVENT;
+ }
+}
diff --git a/src/test/java/org/gitlab4j/api/TestSystemHooksApi.java b/src/test/java/org/gitlab4j/api/TestSystemHooksApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c56c3bb32c9d1bd5ebddb9422db640940f9c8bc
--- /dev/null
+++ b/src/test/java/org/gitlab4j/api/TestSystemHooksApi.java
@@ -0,0 +1,102 @@
+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.Assume.assumeTrue;
+
+import java.util.List;
+
+import org.gitlab4j.api.GitLabApi.ApiVersion;
+import org.gitlab4j.api.models.SystemHook;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+* In order for these tests to run you must set the following properties in test-gitlab4j.properties
+ *
+ * TEST_HOOK_URL
+ * TEST_HOST_URL
+ * TEST_PRIVATE_TOKEN
+ *
+ * If any of the above are NULL, all tests in this class will be skipped.
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TestSystemHooksApi {
+
+ // The following needs to be set to your test repository
+ private static final String TEST_HOST_URL;
+ private static final String TEST_HOOK_URL;
+ private static final String TEST_PRIVATE_TOKEN;
+ static {
+ TEST_HOST_URL = TestUtils.getProperty("TEST_HOST_URL");
+ TEST_HOOK_URL = TestUtils.getProperty("TEST_HOOK_URL");
+ TEST_PRIVATE_TOKEN = TestUtils.getProperty("TEST_PRIVATE_TOKEN");
+ }
+
+ private static final String TEST_SECRET_TOKEN = "123456abcd";
+
+ private static GitLabApi gitLabApi;
+
+ public TestSystemHooksApi() {
+ super();
+ }
+
+ @BeforeClass
+ public static void setup() {
+
+ String problems = "";
+ if (TEST_HOOK_URL == null || TEST_HOOK_URL.trim().length() == 0) {
+ problems += "TEST_HOOK_URL cannot be empty\n";
+ }
+
+ if (TEST_HOST_URL == null || TEST_HOST_URL.trim().length() == 0) {
+ problems += "TEST_HOST_URL cannot be empty\n";
+ }
+
+ if (TEST_PRIVATE_TOKEN == null || TEST_PRIVATE_TOKEN.trim().length() == 0) {
+ problems += "TEST_PRIVATE_TOKEN cannot be empty\n";
+ }
+
+ if (problems.isEmpty()) {
+ gitLabApi = new GitLabApi(ApiVersion.V4, TEST_HOST_URL, TEST_PRIVATE_TOKEN);
+ } else {
+ System.err.print(problems);
+ }
+ }
+
+ @Before
+ public void beforeMethod() {
+ assumeTrue(gitLabApi != null);
+ }
+
+ @Test
+ public void testAddSystemHook() throws GitLabApiException {
+
+ SystemHook hook = gitLabApi.getSystemHooksApi().addSystemHook(TEST_HOOK_URL, TEST_SECRET_TOKEN, true, false, true);
+ assertNotNull(hook);
+ assertEquals(TEST_HOOK_URL, hook.getUrl());
+ assertTrue(hook.getPushEvents());
+ assertFalse(hook.getTagPushEvents());
+ assertTrue(hook.getEnableSslVerification());
+
+ gitLabApi.getSystemHooksApi().deleteSystemHook(hook);
+ }
+
+ @Test
+ public void testGerSystemHooks() throws GitLabApiException {
+
+ SystemHook hook = gitLabApi.getSystemHooksApi().addSystemHook(TEST_HOOK_URL, TEST_SECRET_TOKEN, true, false, true);
+ assertNotNull(hook);
+
+ List hooks = gitLabApi.getSystemHooksApi().getSystemHooks();
+ assertNotNull(hooks);
+ assertFalse(hooks.isEmpty());
+
+ gitLabApi.getSystemHooksApi().deleteSystemHook(hook);
+ }
+}
diff --git a/src/test/resources/org/gitlab4j/api/project-system-hook-event.json b/src/test/resources/org/gitlab4j/api/project-system-hook-event.json
new file mode 100644
index 0000000000000000000000000000000000000000..64c918d47284345bbdb5f4156f93c0d8ab4d5c57
--- /dev/null
+++ b/src/test/resources/org/gitlab4j/api/project-system-hook-event.json
@@ -0,0 +1,12 @@
+{
+ "created_at": "2012-07-21T07:30:54Z",
+ "updated_at": "2012-07-21T07:38:22Z",
+ "event_name": "project_create",
+ "name": "StoreCloud",
+ "owner_email": "johnsmith@gmail.com",
+ "owner_name": "John Smith",
+ "path": "storecloud",
+ "path_with_namespace": "jsmith/storecloud",
+ "project_id": 74,
+ "project_visibility": "private"
+}
\ No newline at end of file
diff --git a/src/test/resources/org/gitlab4j/api/team-member-system-hook-event.json b/src/test/resources/org/gitlab4j/api/team-member-system-hook-event.json
new file mode 100644
index 0000000000000000000000000000000000000000..fdebf50a5e4f739d24686d04cb4a51e2bb2f0ef1
--- /dev/null
+++ b/src/test/resources/org/gitlab4j/api/team-member-system-hook-event.json
@@ -0,0 +1,15 @@
+{
+ "created_at": "2012-07-21T07:30:56Z",
+ "updated_at": "2012-07-21T07:38:22Z",
+ "event_name": "user_add_to_team",
+ "project_access": "Master",
+ "project_id": 74,
+ "project_name": "StoreCloud",
+ "project_path": "storecloud",
+ "project_path_with_namespace": "jsmith/storecloud",
+ "user_email": "johnsmith@gmail.com",
+ "user_name": "John Smith",
+ "user_username": "johnsmith",
+ "user_id": 41,
+ "project_visibility": "private"
+}
\ No newline at end of file