Fixes #538. Use GitLab's host url to calculate project's id.

This commit is contained in:
Alexander Leshkin 2017-05-16 19:10:06 +03:00
parent 43e032e0d7
commit bb5a2caa8b
8 changed files with 361 additions and 187 deletions

View File

@ -0,0 +1,134 @@
package com.dabsquared.gitlabjenkins.gitlab;
import java.util.List;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApiClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Label;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.api.model.User;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
/**
* Implements {@link GitLabApi} by delegating REST-client calls to RESTEasy proxy.
*
* @author Alexander Leshkin
*
*/
class GitLabApiExtension implements GitLabApi {
private GitLabApiClient client;
private String gitlabHostUrl;
public GitLabApiExtension(GitLabApiClient client, String gitlabHostUrl) {
this.client = client;
this.gitlabHostUrl = gitlabHostUrl;
}
@Override
public String getGitLabHostUrl() {
return gitlabHostUrl;
}
@Override
public Project createProject(String projectName) {
return client.createProject(projectName);
}
@Override
public void createMergeRequest(Integer projectId, String sourceBranch, String targetBranch, String title) {
client.createMergeRequest(projectId, sourceBranch, targetBranch, title);
}
@Override
public Project getProject(String projectName) {
return client.getProject(projectName);
}
@Override
public Project updateProject(String projectId, String name, String path) {
return client.updateProject(projectId, name, path);
}
@Override
public void deleteProject(String projectId) {
client.deleteProject(projectId);
}
@Override
public void addProjectHook(String projectId, String url, Boolean pushEvents, Boolean mergeRequestEvents,
Boolean noteEvents) {
client.addProjectHook(projectId, url, pushEvents, mergeRequestEvents, noteEvents);
}
@Override
public void changeBuildStatus(String projectId, String sha, BuildState state, String ref, String context,
String targetUrl, String description) {
client.changeBuildStatus(projectId, sha, state, ref, context, targetUrl, description);
}
@Override
public void changeBuildStatus(Integer projectId, String sha, BuildState state, String ref, String context,
String targetUrl, String description) {
client.changeBuildStatus(projectId, sha, state, ref, context, targetUrl, description);
}
@Override
public void getCommit(String projectId, String sha) {
client.getCommit(projectId, sha);
}
@Override
public void acceptMergeRequest(Integer projectId, Integer mergeRequestId, String mergeCommitMessage,
boolean shouldRemoveSourceBranch) {
client.acceptMergeRequest(projectId, mergeRequestId, mergeCommitMessage, shouldRemoveSourceBranch);
}
@Override
public void createMergeRequestNote(Integer projectId, Integer mergeRequestId, String body) {
client.createMergeRequestNote(projectId, mergeRequestId, body);
}
@Override
public List<MergeRequest> getMergeRequests(String projectId, State state, int page, int perPage) {
return client.getMergeRequests(projectId, state, page, perPage);
}
@Override
public List<Branch> getBranches(String projectId) {
return client.getBranches(projectId);
}
@Override
public Branch getBranch(String projectId, String branch) {
return client.getBranch(projectId, branch);
}
@Override
public void headCurrentUser() {
client.headCurrentUser();
}
@Override
public User getCurrentUser() {
return client.getCurrentUser();
}
@Override
public User addUser(String email, String username, String name, String password) {
return client.addUser(email, username, name, password);
}
@Override
public User updateUser(String userId, String email, String username, String name, String password) {
return client.updateUser(userId, email, username, name, password);
}
@Override
public List<Label> getLabels(String projectId) {
return client.getLabels(projectId);
}
}

View File

@ -6,6 +6,7 @@ import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.dabsquared.gitlabjenkins.connection.GitLabApiToken;
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApiClient;
import com.dabsquared.gitlabjenkins.util.JsonUtil;
import com.dabsquared.gitlabjenkins.util.LoggerUtil;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@ -82,7 +83,7 @@ public class GitLabClientBuilder {
proxyConfiguration.getPassword());
}
return builder
GitLabApiClient apiClient = builder
.connectionPoolSize(60)
.maxPooledPerRoute(30)
.establishConnectionTimeout(connectionTimeout, TimeUnit.SECONDS)
@ -94,9 +95,10 @@ public class GitLabClientBuilder {
.register(new RemoveAcceptEncodingFilter())
.register(new JaxrsFormProvider())
.build().target(gitlabHostUrl)
.proxyBuilder(GitLabApi.class)
.classloader(GitLabApi.class.getClassLoader())
.proxyBuilder(GitLabApiClient.class)
.classloader(GitLabApiClient.class.getClassLoader())
.build();
return new GitLabApiExtension(apiClient, gitlabHostUrl);
}
public static GitLabApi buildClient(GitLabConnection connection) {

View File

@ -1,173 +1,14 @@
package com.dabsquared.gitlabjenkins.gitlab.api;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Label;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.api.model.User;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.List;
/**
* @author Robin Müller
* Extends REST-client interface to provide additional methods for plugin's code.
*
* @author Alexander Leshkin
*
*/
@Path("/api/v3")
public interface GitLabApi {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects")
Project createProject(@FormParam("name") String projectName);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests")
void createMergeRequest(
@PathParam("projectId") Integer projectId,
@FormParam("source_branch") String sourceBranch,
@FormParam("target_branch") String targetBranch,
@FormParam("title") String title);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectName}")
Project getProject(@PathParam("projectName") String projectName);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}")
Project updateProject(@PathParam("projectId") String projectId,
@FormParam("name") String name,
@FormParam("path") String path);
@DELETE
@Path("/projects/{projectId}")
void deleteProject(@PathParam("projectId") String projectId);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/hooks")
void addProjectHook(@PathParam("projectId") String projectId,
@FormParam("url") String url,
@FormParam("push_events") Boolean pushEvents,
@FormParam("merge_requests_events") Boolean mergeRequestEvents,
@FormParam("note_events") Boolean noteEvents);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}")
void changeBuildStatus(@PathParam("projectId") String projectId,
@PathParam("sha") String sha,
@FormParam("state") BuildState state,
@FormParam("ref") String ref,
@FormParam("context") String context,
@FormParam("target_url") String targetUrl,
@FormParam("description") String description);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}")
void changeBuildStatus(@PathParam("projectId") Integer projectId,
@PathParam("sha") String sha,
@FormParam("state") BuildState state,
@FormParam("ref") String ref,
@FormParam("context") String context,
@FormParam("target_url") String targetUrl,
@FormParam("description") String description);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/commits/{sha}")
void getCommit(@PathParam("projectId") String projectId, @PathParam("sha") String sha);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/merge")
void acceptMergeRequest(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestId") Integer mergeRequestId,
@FormParam("merge_commit_message") String mergeCommitMessage,
@FormParam("should_remove_source_branch") boolean shouldRemoveSourceBranch);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/notes")
void createMergeRequestNote(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestId") Integer mergeRequestId,
@FormParam("body") String body);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/merge_requests")
List<MergeRequest> getMergeRequests(@PathParam("projectId") String projectId,
@QueryParam("state") State state,
@QueryParam("page") int page,
@QueryParam("per_page") int perPage);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches")
List<Branch> getBranches(@PathParam("projectId") String projectId);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches/{branch}")
Branch getBranch(@PathParam("projectId") String projectId,
@PathParam("branch") String branch);
@HEAD
@Produces(MediaType.APPLICATION_JSON)
@Path("/user")
void headCurrentUser();
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/user")
User getCurrentUser();
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users")
User addUser(@FormParam("email") String email,
@FormParam("username") String username,
@FormParam("name") String name,
@FormParam("password") String password);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users/{userId}")
User updateUser(@PathParam("userId") String userId,
@FormParam("email") String email,
@FormParam("username") String username,
@FormParam("name") String name,
@FormParam("password") String password);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/labels")
List<Label> getLabels(@PathParam("projectId") String projectId);
public interface GitLabApi extends GitLabApiClient {
/**
* Returns GitLab host base url from plugin confugruation.
*/
String getGitLabHostUrl();
}

View File

@ -0,0 +1,173 @@
package com.dabsquared.gitlabjenkins.gitlab.api;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Branch;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Label;
import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.api.model.User;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.List;
/**
* @author Robin Müller
*/
@Path("/api/v3")
public interface GitLabApiClient {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects")
Project createProject(@FormParam("name") String projectName);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests")
void createMergeRequest(
@PathParam("projectId") Integer projectId,
@FormParam("source_branch") String sourceBranch,
@FormParam("target_branch") String targetBranch,
@FormParam("title") String title);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectName}")
Project getProject(@PathParam("projectName") String projectName);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}")
Project updateProject(@PathParam("projectId") String projectId,
@FormParam("name") String name,
@FormParam("path") String path);
@DELETE
@Path("/projects/{projectId}")
void deleteProject(@PathParam("projectId") String projectId);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/hooks")
void addProjectHook(@PathParam("projectId") String projectId,
@FormParam("url") String url,
@FormParam("push_events") Boolean pushEvents,
@FormParam("merge_requests_events") Boolean mergeRequestEvents,
@FormParam("note_events") Boolean noteEvents);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}")
void changeBuildStatus(@PathParam("projectId") String projectId,
@PathParam("sha") String sha,
@FormParam("state") BuildState state,
@FormParam("ref") String ref,
@FormParam("context") String context,
@FormParam("target_url") String targetUrl,
@FormParam("description") String description);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/statuses/{sha}")
void changeBuildStatus(@PathParam("projectId") Integer projectId,
@PathParam("sha") String sha,
@FormParam("state") BuildState state,
@FormParam("ref") String ref,
@FormParam("context") String context,
@FormParam("target_url") String targetUrl,
@FormParam("description") String description);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/commits/{sha}")
void getCommit(@PathParam("projectId") String projectId, @PathParam("sha") String sha);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/merge")
void acceptMergeRequest(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestId") Integer mergeRequestId,
@FormParam("merge_commit_message") String mergeCommitMessage,
@FormParam("should_remove_source_branch") boolean shouldRemoveSourceBranch);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/notes")
void createMergeRequestNote(@PathParam("projectId") Integer projectId,
@PathParam("mergeRequestId") Integer mergeRequestId,
@FormParam("body") String body);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/merge_requests")
List<MergeRequest> getMergeRequests(@PathParam("projectId") String projectId,
@QueryParam("state") State state,
@QueryParam("page") int page,
@QueryParam("per_page") int perPage);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches")
List<Branch> getBranches(@PathParam("projectId") String projectId);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/repository/branches/{branch}")
Branch getBranch(@PathParam("projectId") String projectId,
@PathParam("branch") String branch);
@HEAD
@Produces(MediaType.APPLICATION_JSON)
@Path("/user")
void headCurrentUser();
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/user")
User getCurrentUser();
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users")
User addUser(@FormParam("email") String email,
@FormParam("username") String username,
@FormParam("name") String name,
@FormParam("password") String password);
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/users/{userId}")
User updateUser(@PathParam("userId") String userId,
@FormParam("email") String email,
@FormParam("username") String username,
@FormParam("name") String name,
@FormParam("password") String password);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/projects/{projectId}/labels")
List<Label> getLabels(@PathParam("projectId") String projectId);
}

View File

@ -65,7 +65,7 @@ public class GitLabProjectBranchesService {
@Override
public List<String> call() throws Exception {
List<String> result = new ArrayList<>();
String projectId = ProjectIdUtil.retrieveProjectId(sourceRepository);
String projectId = ProjectIdUtil.retrieveProjectId(client, sourceRepository);
for (Branch branch : client.getBranches(projectId)) {
result.add(branch.getName());
}

View File

@ -65,7 +65,7 @@ public class GitLabProjectLabelsService {
@Override
public List<String> call() throws Exception {
List<String> result = new ArrayList<>();
String projectId = ProjectIdUtil.retrieveProjectId(sourceRepository);
String projectId = ProjectIdUtil.retrieveProjectId(client, sourceRepository);
for (Label label : client.getLabels(projectId)) {
result.add(label.getName());
}

View File

@ -2,6 +2,8 @@ package com.dabsquared.gitlabjenkins.util;
import org.eclipse.jgit.transport.URIish;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import java.net.URISyntaxException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -11,13 +13,23 @@ import java.util.regex.Pattern;
*/
public final class ProjectIdUtil {
private static final Pattern PROJECT_ID_PATTERN = Pattern.compile("^/?(.*/)?(?<projectId>.*/.*)(\\.git)$");
private static final Pattern PROJECT_ID_PATTERN = Pattern.compile("^/?(?<projectId>.*)(\\.git)$");
private ProjectIdUtil() { }
public static String retrieveProjectId(String remoteUrl) throws ProjectIdResolutionException {
public static String retrieveProjectId(GitLabApi client, String remoteUrl) throws ProjectIdResolutionException {
try {
String projectId = new URIish(remoteUrl).getPath();
String projectId = null;
String baseUri = client.getGitLabHostUrl();
if (baseUri != null && remoteUrl.startsWith(baseUri)) {
projectId = new URIish(remoteUrl.substring(baseUri.length(), remoteUrl.length())).getPath();
} else {
projectId = new URIish(remoteUrl).getPath();
}
if (projectId.startsWith(":")) {
projectId = projectId.substring(1);
}
Matcher matcher = PROJECT_ID_PATTERN.matcher(projectId);
if (matcher.matches()) {
return matcher.group("projectId");

View File

@ -1,13 +1,17 @@
package com.dabsquared.gitlabjenkins.util;
import static com.dabsquared.gitlabjenkins.util.ProjectIdUtilTest.TestData.forRemoteUrl;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import static com.dabsquared.gitlabjenkins.util.ProjectIdUtilTest.TestData.forRemoteUrl;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
/**
* @author Robin Müller
@ -17,14 +21,20 @@ public class ProjectIdUtilTest {
@DataPoints
public static TestData[] testData = {
forRemoteUrl("git@gitlab.com:test/project.git").expectProjectId("test/project"),
forRemoteUrl("https://gitlab.com/test/project.git").expectProjectId("test/project"),
forRemoteUrl("https://myurl.com/gitlab/group/project.git").expectProjectId("group/project")
forRemoteUrl("git@gitlab.com", "git@gitlab.com:test/project.git").expectProjectId("test/project"),
forRemoteUrl("https://gitlab.com", "https://gitlab.com/test/project.git").expectProjectId("test/project"),
forRemoteUrl("https://myurl.com/gitlab", "https://myurl.com/gitlab/group/project.git").expectProjectId("group/project"),
forRemoteUrl("git@gitlab.com", "git@gitlab.com:group/subgroup/project.git").expectProjectId("group/subgroup/project"),
forRemoteUrl("https://myurl.com/gitlab", "https://myurl.com/gitlab/group/subgroup/project.git").expectProjectId("group/subgroup/project"),
forRemoteUrl("https://myurl.com", "https://myurl.com/group/subgroup/project.git").expectProjectId("group/subgroup/project"),
};
@Theory
public void retrieveProjectId(TestData testData) throws ProjectIdUtil.ProjectIdResolutionException {
String projectId = ProjectIdUtil.retrieveProjectId(testData.remoteUrl);
GitLabApi client = mock(GitLabApi.class);
when(client.getGitLabHostUrl()).thenReturn(testData.baseUrl);
String projectId = ProjectIdUtil.retrieveProjectId(client, testData.remoteUrl);
assertThat(projectId, is(testData.expectedProjectId));
}
@ -32,10 +42,12 @@ public class ProjectIdUtilTest {
static final class TestData {
private final String baseUrl;
private final String remoteUrl;
private String expectedProjectId;
private TestData(String remoteUrl) {
private TestData(String baseUrl, String remoteUrl) {
this.baseUrl = baseUrl;
this.remoteUrl = remoteUrl;
}
@ -49,8 +61,8 @@ public class ProjectIdUtilTest {
return remoteUrl;
}
static TestData forRemoteUrl(String remoteUrl) {
return new TestData(remoteUrl);
static TestData forRemoteUrl(String baseUrl, String remoteUrl) {
return new TestData(baseUrl, remoteUrl);
}
}
}