Fix to support NameSpace or ProjectName with dot in it

According to Rails documentation(http://guides.rubyonrails.org/v3.1.3/routing.html section 3.2 Dynamic Segments) Rail use a dot as separator for formatted routes.
In this case if we have dot in ProjectId constructed from(namespace + projectname) all request except getSingleProject(http://docs.gitlab.com/ce/api/projects.html#get-single-project) fail with message
"Failed to update Gitlab commit status for project Name.Test/Repo': HTTP 500 Internal Server Error"

To support This case we check if projectId have dot inside of it
if it has execute getSingleProject request and extract Integer projectId to operate with
if it hasn't use prepared projectId(Namespace + ProjectName)
This commit is contained in:
Konstantin Bulanov 2016-05-18 12:38:04 +04:00
parent 1adc6830ba
commit fad9c5cfcd
3 changed files with 123 additions and 14 deletions

View File

@ -4,20 +4,24 @@ import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import hudson.EnvVars;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.git.util.BuildData;
import jenkins.model.Jenkins;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.WebApplicationException;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.WebApplicationException;
import hudson.EnvVars;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.git.util.BuildData;
import jenkins.model.Jenkins;
/**
* @author Robin Müller
*/
@ -26,15 +30,17 @@ public class CommitStatusUpdater {
private final static Logger LOGGER = Logger.getLogger(CommitStatusUpdater.class.getName());
public static void updateCommitStatus(Run<?, ?> build, TaskListener listener, BuildState state) {
GitLabApi client = getClient(build);
if (client == null) {
println(listener, "No GitLab connection configured");
return;
}
String commitHash = getBuildRevision(build);
String buildUrl = getBuildUrl(build);
try {
for (String gitlabProjectId : retrieveGitlabProjectIds(build, build.getEnvironment(listener))) {
try {
GitLabApi client = getClient(build);
if (client == null) {
println(listener, "No GitLab connection configured");
} else if (existsCommit(client, gitlabProjectId, commitHash)) {
if (existsCommit(client, gitlabProjectId, commitHash)) {
client.changeBuildStatus(gitlabProjectId, commitHash, state, getBuildBranch(build), "jenkins", buildUrl, null);
}
} catch (WebApplicationException e) {
@ -98,9 +104,20 @@ public class CommitStatusUpdater {
private static List<String> retrieveGitlabProjectIds(Run<?, ?> build, EnvVars environment) {
List<String> result = new ArrayList<>();
GitLabApi gitLabClient = getClient(build);
if (gitLabClient == null) {
return result;
}
for (String remoteUrl : build.getAction(BuildData.class).getRemoteUrls()) {
try {
result.add(ProjectIdUtil.retrieveProjectId(environment.expand(remoteUrl)));
String projectNameWithNameSpace = ProjectIdUtil.retrieveProjectId(environment.expand(remoteUrl));
if (StringUtils.isNotBlank(projectNameWithNameSpace)) {
String projectId = projectNameWithNameSpace;
if (projectNameWithNameSpace.contains(".")) {
projectId = gitLabClient.getProject(projectNameWithNameSpace).getId().toString();
}
result.add(projectId);
}
} catch (ProjectIdUtil.ProjectIdResolutionException e) {
// nothing to do
}

View File

@ -21,6 +21,8 @@ import hudson.plugins.git.util.Build;
import hudson.plugins.git.util.BuildData;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import org.apache.commons.io.IOUtils;
import org.eclipse.jgit.lib.ObjectId;
import org.hamcrest.CoreMatchers;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
@ -34,6 +36,7 @@ import org.mockito.stubbing.Answer;
import org.mockserver.client.server.MockServerClient;
import org.mockserver.junit.MockServerRule;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.mockserver.verify.VerificationTimes;
import java.io.ByteArrayOutputStream;
@ -94,8 +97,8 @@ public class GitLabCommitStatusPublisherTest {
@Test
public void running() throws UnsupportedEncodingException {
HttpRequest[] requests = new HttpRequest[] {
prepareExistsCommitWithSuccessResponse("test/project", "123abc"),
prepareUpdateCommitStatusWithSuccessResponse("test/project", "123abc", jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running)
prepareExistsCommitWithSuccessResponse("test/project", "123abc"),
prepareUpdateCommitStatusWithSuccessResponse("test/project", "123abc", jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running)
};
AbstractBuild build = mockBuild("123abc", "/build/123", GIT_LAB_CONNECTION, null, "test/project");
@ -105,6 +108,21 @@ public class GitLabCommitStatusPublisherTest {
mockServerClient.verify(requests);
}
@Test
public void runningWithDotInProjectId() throws IOException {
HttpRequest[] requests = new HttpRequest[] {
prepareGetProjectResponse("test/project.test",1),
prepareExistsCommitWithSuccessResponse("1", "123abc"),
prepareUpdateCommitStatusWithSuccessResponse("1", "123abc", jenkins.getInstance().getRootUrl() + "/build/123", BuildState.running)
};
AbstractBuild build = mockBuild("123abc", "/build/123", GIT_LAB_CONNECTION, null, "test/project.test");
GitLabCommitStatusPublisher publisher = new GitLabCommitStatusPublisher();
publisher.prebuild(build, listener);
mockServerClient.verify(requests);
}
@Test
public void canceled() throws IOException, InterruptedException {
HttpRequest[] requests = new HttpRequest[] {
@ -198,6 +216,7 @@ public class GitLabCommitStatusPublisherTest {
return updateCommitStatus;
}
private HttpRequest prepareUpdateCommitStatus(String projectId, String sha, String targetUrl, BuildState state) throws UnsupportedEncodingException {
return request()
.withPath("/gitlab/api/v3/projects/" + URLEncoder.encode(projectId, "UTF-8") + "/statuses/" + sha)
@ -221,6 +240,19 @@ public class GitLabCommitStatusPublisherTest {
.withHeader("PRIVATE-TOKEN", "secret");
}
private HttpRequest prepareGetProjectResponse(String projectName, int projectId) throws IOException {
HttpRequest request= request()
.withPath("/gitlab/api/v3/projects/" + URLEncoder.encode(projectName, "UTF-8"))
.withMethod("GET")
. withHeader("PRIVATE-TOKEN", "secret");
HttpResponse response = response().withBody(getSingleProjectJson("GetSingleProject.json",projectName,projectId));
response.withHeader("Content-Type", "application/json");
mockServerClient.when(request).respond(response.withStatusCode(200));
return request;
}
private AbstractBuild mockBuild(String sha, String buildUrl, String gitLabConnection, Result result, String... remoteUrls) {
AbstractBuild build = mock(AbstractBuild.class);
BuildData buildData = mock(BuildData.class);
@ -251,4 +283,12 @@ public class GitLabCommitStatusPublisherTest {
}
return build;
}
private String getSingleProjectJson(String name,String projectNameWithNamespace, int porjectId) throws IOException {
String nameSpace = projectNameWithNamespace.split("/")[0];
String projectName = projectNameWithNamespace.split("/")[1];
return IOUtils.toString(getClass().getResourceAsStream(name))
.replace("${projectId}", porjectId + "")
.replace("${nameSpace}", nameSpace)
.replace("${projectName}", projectName);
}
}

View File

@ -0,0 +1,52 @@
{
"id": "${projectId}",
"description": "",
"default_branch": "master",
"tag_list": [],
"public": false,
"archived": false,
"visibility_level": 10,
"ssh_url_to_repo": "git@localhost:${nameSpace}/${projectName}.git",
"http_url_to_repo": "https://localhost/${nameSpace}/${projectName}.git",
"web_url": "https://localhost/${nameSpace}/${projectName}",
"name": "${projectName}",
"name_with_namespace": "${nameSpace} / ${projectName}",
"path": "${projectName}",
"path_with_namespace": "${nameSpace}/${projectName}",
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"snippets_enabled": false,
"created_at": "2016-04-08T13:20:40.951Z",
"last_activity_at": "2016-05-18T12:55:15.322Z",
"shared_runners_enabled": true,
"creator_id": 1,
"namespace": {
"id": 159,
"name": "${nameSpace}",
"path": "${nameSpace}",
"owner_id": null,
"created_at": "2016-04-08T13:18:08.233Z",
"updated_at": "2016-04-08T13:18:08.233Z",
"description": "",
"avatar": {
"url": null
},
"share_with_group_lock": false,
"visibility_level": 20
},
"avatar_url": null,
"star_count": 2,
"forks_count": 0,
"open_issues_count": 0,
"runners_token": "nR-uhvbge5MTFyBimJ7D",
"public_builds": true,
"permissions": {
"project_access": {
"access_level": 40,
"notification_level": 3
},
"group_access": null
}
}