From aea794327cb2b39f3faa764a8f1eb70b5d6f007c Mon Sep 17 00:00:00 2001 From: Greg Messner Date: Sat, 5 Aug 2017 18:08:59 -0700 Subject: [PATCH] Mods to support updateProject(). --- .../java/org/gitlab4j/api/AbstractApi.java | 24 ++++- .../org/gitlab4j/api/GitLabApiClient.java | 29 +++++++ .../java/org/gitlab4j/api/ProjectApi.java | 87 ++++++++++++++++++- .../java/org/gitlab4j/api/models/Project.java | 5 ++ .../java/org/gitlab4j/api/TestProjectApi.java | 71 ++++++++++++--- 5 files changed, 202 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/gitlab4j/api/AbstractApi.java b/src/main/java/org/gitlab4j/api/AbstractApi.java index c02a7c73..9083a265 100644 --- a/src/main/java/org/gitlab4j/api/AbstractApi.java +++ b/src/main/java/org/gitlab4j/api/AbstractApi.java @@ -40,7 +40,11 @@ public abstract class AbstractApi implements Constants { protected String urlEncode(String s) throws GitLabApiException { try { - return (URLEncoder.encode(s, "UTF-8")); + String encoded = URLEncoder.encode(s, "UTF-8"); + encoded = encoded.replace(".", "%2E"); + encoded = encoded.replace("-", "%2D"); + encoded = encoded.replace("_", "%5F"); + return (encoded); } catch (Exception e) { throw new GitLabApiException(e); } @@ -191,6 +195,24 @@ public abstract class AbstractApi implements Constants { } } + /** + * Perform an HTTP PUT call with the specified form data and path objects, returning + * a ClientResponse instance with the data returned from the endpoint. + * + * @param expectedStatus the HTTP status that should be returned from the server + * @param formData the Form containing the name/value pairs for the POST data + * @param pathArgs variable list of arguments used to build the URI + * @return a ClientResponse instance with the data returned from the endpoint + * @throws GitLabApiException if any exception occurs during execution + */ + protected Response putWithFormData(Response.Status expectedStatus, Form formData, Object... pathArgs) throws GitLabApiException { + try { + return validate(getApiClient().put(formData, pathArgs), expectedStatus); + } catch (Exception e) { + throw handle(e); + } + } + /** * Perform an HTTP DELETE call with the specified form data and path objects, returning * a ClientResponse instance with the data returned from the endpoint. diff --git a/src/main/java/org/gitlab4j/api/GitLabApiClient.java b/src/main/java/org/gitlab4j/api/GitLabApiClient.java index e5380ab3..b288a74d 100644 --- a/src/main/java/org/gitlab4j/api/GitLabApiClient.java +++ b/src/main/java/org/gitlab4j/api/GitLabApiClient.java @@ -394,6 +394,35 @@ public class GitLabApiClient { return (invocation(url, null).put(Entity.entity(queryParams, MediaType.APPLICATION_FORM_URLENCODED_TYPE))); } + /** + * Perform an HTTP PUT call with the specified form data and path objects, returning + * a ClientResponse instance with the data returned from the endpoint. + * + * @param formData the Form containing the name/value pairs + * @param pathArgs variable list of arguments used to build the URI + * @return a ClientResponse instance with the data returned from the endpoint + * @throws IOException if an error occurs while constructing the URL + */ + protected Response put(Form formData, Object... pathArgs) throws IOException { + URL url = getApiUrl(pathArgs); + return put(formData, url); + } + + /** + * Perform an HTTP PUT call with the specified form data and URL, returning + * a ClientResponse instance with the data returned from the endpoint. + * + * @param formData the Form containing the name/value pairs + * @param url the fully formed path to the GitLab API endpoint + * @return a ClientResponse instance with the data returned from the endpoint + */ + protected Response put(Form formData, URL url) { + if (formData instanceof GitLabApiForm) + return (invocation(url, null).put(Entity.entity(formData.asMap(), MediaType.APPLICATION_FORM_URLENCODED_TYPE))); + else + return (invocation(url, null).put(Entity.entity(formData, MediaType.APPLICATION_FORM_URLENCODED_TYPE))); + } + /** * Perform an HTTP DELETE call with the specified form data and path objects, returning * a Response instance with the data returned from the endpoint. diff --git a/src/main/java/org/gitlab4j/api/ProjectApi.java b/src/main/java/org/gitlab4j/api/ProjectApi.java index a4b25418..c7193579 100644 --- a/src/main/java/org/gitlab4j/api/ProjectApi.java +++ b/src/main/java/org/gitlab4j/api/ProjectApi.java @@ -658,7 +658,7 @@ public class ProjectApi extends AbstractApi implements Constants { * @param importUrl The Import URL for the project, otherwise null * @return the GitLab Project * @throws GitLabApiException if any exception occurs - * @deprecated As of release 4.2.0, replaced by {@link #createProject(String, Integer, String, Boolean, Boolean, + * @deprecated As of release 4.2.0, replaced by {@link #createProject(String, Integer, String, Boolean, Boolean, * Boolean, Boolean, Visibility, Integer, String)} */ @Deprecated @@ -690,6 +690,91 @@ public class ProjectApi extends AbstractApi implements Constants { return (response.readEntity(Project.class)); } + /** + * Updates a project. The following properties on the Project instance + * are utilized in the edit of the project, null values are not updated: + * + * id (required) - existing project id + * name (required) - project name + * path (optional) - project path + * defaultBranch (optional) - master by default + * description (optional) - short project description + * visibility (optional) - Limit by visibility public, internal, or private + * issuesEnabled (optional) - Enable issues for this project + * mergeRequestsEnabled (optional) - Enable merge requests for this project + * wikiEnabled (optional) - Enable wiki for this project + * snippetsEnabled (optional) - Enable snippets for this project + * jobsEnabled (optional) - Enable jobs for this project + * containerRegistryEnabled (optional) - Enable container registry for this project + * sharedRunnersEnabled (optional) - Enable shared runners for this project + * publicJobs (optional) - If true, jobs can be viewed by non-project-members + * onlyAllowMergeIfPipelineSucceeds (optional) - Set whether merge requests can only be merged with successful jobs + * onlyAllowMergeIfAllDiscussionsAreResolved (optional) - Set whether merge requests can only be merged when all the discussions are resolved + * lLfsEnabled (optional) - Enable LFS + * requestAccessEnabled (optional) - Allow users to request member access + * repositoryStorage (optional) - Which storage shard the repository is on. Available only to admins + * approvalsBeforeMerge (optional) - How many approvers should approve merge request by default + * + * NOTE: The following parameters specified by the GitLab API edit project are not supported: + * import_url + * tag_list array + * avatar + * ci_config_path + * + * @param project the Project instance with the configuration for the new project + * @return a Project instance with the newly updated project info + * @throws GitLabApiException if any exception occurs + */ + public Project updateProject(Project project) throws GitLabApiException { + + if (project == null) { + throw new RuntimeException("Project instance cannot be null."); + } + + Integer id = project.getId(); + if (id == null) { + throw new RuntimeException("Project ID cannot be null."); + } + + String name = project.getName(); + if (name == null || name.trim().length() == 0) { + throw new RuntimeException("Project name cannot be null or empty."); + } + + GitLabApiForm formData = new GitLabApiForm() + .withParam("name", name, true) + .withParam("path", project.getPath()) + .withParam("default_branch", project.getDefaultBranch()) + .withParam("description", project.getDescription()) + .withParam("issues_enabled", project.getIssuesEnabled()) + .withParam("merge_requests_enabled", project.getMergeRequestsEnabled()) + .withParam("jobs_enabled", project.getJobsEnabled()) + .withParam("wiki_enabled", project.getWikiEnabled()) + .withParam("snippets_enabled", project.getSnippetsEnabled()) + .withParam("container_registry_enabled", project.getContainerRegistryEnabled()) + .withParam("shared_runners_enabled", project.getSharedRunnersEnabled()) + .withParam("public_jobs", project.getPublicJobs()) + .withParam("only_allow_merge_if_pipeline_succeeds", project.getOnlyAllowMergeIfPipelineSucceeds()) + .withParam("only_allow_merge_if_all_discussions_are_resolved", project.getOnlyAllowMergeIfAllDiscussionsAreResolved()) + .withParam("lfs_enabled", project.getLfsEnabled()) + .withParam("request_access_enabled", project.getRequestAccessEnabled()) + .withParam("repository_storage", project.getRepositoryStorage()) + .withParam("approvals_before_merge", project.getApprovalsBeforeMerge()); + + if (isApiVersion(ApiVersion.V3)) { + formData.withParam("visibility_level", project.getVisibilityLevel()); + boolean isPublic = (project.getPublic() != null ? project.getPublic() : project.getVisibility() == Visibility.PUBLIC); + formData.withParam("public", isPublic); + } else { + Visibility visibility = (project.getVisibility() != null ? project.getVisibility() : + project.getPublic() == Boolean.TRUE ? Visibility.PUBLIC : null); + formData.withParam("visibility", visibility); + } + + Response response = putWithFormData(Response.Status.OK, formData, "projects", id); + return (response.readEntity(Project.class)); + } + /** * Removes project with all resources(issues, merge requests etc). * diff --git a/src/main/java/org/gitlab4j/api/models/Project.java b/src/main/java/org/gitlab4j/api/models/Project.java index faceb633..5aa7031e 100644 --- a/src/main/java/org/gitlab4j/api/models/Project.java +++ b/src/main/java/org/gitlab4j/api/models/Project.java @@ -172,6 +172,11 @@ public class Project { this.id = id; } + public Project withId(Integer id) { + this.id = id; + return (this); + } + public Boolean getIssuesEnabled() { return issuesEnabled; } diff --git a/src/test/java/org/gitlab4j/api/TestProjectApi.java b/src/test/java/org/gitlab4j/api/TestProjectApi.java index 5d828b1f..c550811d 100644 --- a/src/test/java/org/gitlab4j/api/TestProjectApi.java +++ b/src/test/java/org/gitlab4j/api/TestProjectApi.java @@ -47,6 +47,7 @@ public class TestProjectApi { private static final String TEST_PROJECT_NAME_1 = "test-gitlab4j-create-project"; private static final String TEST_PROJECT_NAME_2 = "test-gitlab4j-create-project-2"; + private static final String TEST_PROJECT_NAME_UPDATE = "test-gitlab4j-create-project-update"; private static GitLabApi gitLabApi; public TestProjectApi() { @@ -75,27 +76,30 @@ public class TestProjectApi { System.err.print(problems); } - if (gitLabApi != null) { - try { - Project project = gitLabApi.getProjectApi().getProject(TEST_NAMESPACE, TEST_PROJECT_NAME_1); - gitLabApi.getProjectApi().deleteProject(project); - project = gitLabApi.getProjectApi().getProject(TEST_NAMESPACE, TEST_PROJECT_NAME_2); - gitLabApi.getProjectApi().deleteProject(project); - } catch (GitLabApiException ignore) { - } - } + deleteAllTestProjects(); } @AfterClass public static void teardown() throws GitLabApiException { + deleteAllTestProjects(); + } + + private static void deleteAllTestProjects() { if (gitLabApi != null) { try { Project project = gitLabApi.getProjectApi().getProject(TEST_NAMESPACE, TEST_PROJECT_NAME_1); gitLabApi.getProjectApi().deleteProject(project); - project = gitLabApi.getProjectApi().getProject(TEST_NAMESPACE, TEST_PROJECT_NAME_2); + } catch (GitLabApiException ignore) {} + + try { + Project project = gitLabApi.getProjectApi().getProject(TEST_NAMESPACE, TEST_PROJECT_NAME_2); + gitLabApi.getProjectApi().deleteProject(project); + } catch (GitLabApiException ignore) {} + + try { + Project project = gitLabApi.getProjectApi().getProject(TEST_NAMESPACE, TEST_PROJECT_NAME_UPDATE); gitLabApi.getProjectApi().deleteProject(project); - } catch (GitLabApiException ignore) { - } + } catch (GitLabApiException ignore) {} } } @@ -127,6 +131,49 @@ public class TestProjectApi { assertTrue(Visibility.PUBLIC == newProject.getVisibility() || Boolean.TRUE == newProject.getPublic()); } + @Test + public void testUpdate() throws GitLabApiException { + + Project project = new Project() + .withName(TEST_PROJECT_NAME_UPDATE) + .withDescription("GitLab4J test project.") + .withIssuesEnabled(true) + .withMergeRequestsEnabled(true) + .withWikiEnabled(true) + .withSnippetsEnabled(true) + .withVisibility(Visibility.PUBLIC); + + Project newProject = gitLabApi.getProjectApi().createProject(project); + assertNotNull(newProject); + assertEquals(project.getName(), newProject.getName()); + assertEquals(project.getDescription(), newProject.getDescription()); + assertEquals(project.getIssuesEnabled(), newProject.getIssuesEnabled()); + assertEquals(project.getMergeRequestsEnabled(), newProject.getMergeRequestsEnabled()); + assertEquals(project.getWikiEnabled(), newProject.getWikiEnabled()); + assertEquals(project.getSnippetsEnabled(), newProject.getSnippetsEnabled()); + assertTrue(Visibility.PUBLIC == newProject.getVisibility() || Boolean.TRUE == newProject.getPublic()); + + project = new Project() + .withId(newProject.getId()) + .withName(newProject.getName()) + .withDescription("GitLab4J test updateProject()") + .withIssuesEnabled(false) + .withMergeRequestsEnabled(false) + .withWikiEnabled(false) + .withSnippetsEnabled(false) + .withVisibility(Visibility.PRIVATE); + + Project updatedProject = gitLabApi.getProjectApi().updateProject(project); + assertNotNull(updatedProject); + assertEquals(project.getName(), newProject.getName()); + assertEquals(project.getDescription(), updatedProject.getDescription()); + assertEquals(project.getIssuesEnabled(), updatedProject.getIssuesEnabled()); + assertEquals(project.getMergeRequestsEnabled(), updatedProject.getMergeRequestsEnabled()); + assertEquals(project.getWikiEnabled(), updatedProject.getWikiEnabled()); + assertEquals(project.getSnippetsEnabled(), updatedProject.getSnippetsEnabled()); + assertTrue(Visibility.PRIVATE == updatedProject.getVisibility() || Boolean.FALSE == updatedProject.getPublic()); + } + @Test public void testListProjects() throws GitLabApiException { -- GitLab